commit 5dd6d8579b3a6d98ee5155c809e18fabe40211ea Author: Valerie Date: Wed May 18 07:46:03 2022 -0400 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +target diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..a82eed7 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "java", + "name": "Launch App", + "request": "launch", + "mainClass": "xyz.valnet.hadean.HadeanGame", + "projectName": "hadean", + "console": "internalConsole", + "internalConsoleOptions": "neverOpen", + "osx": { + "vmArgs": "-XstartOnFirstThread" + } + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e0f15db --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic" +} \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..b88eb52 --- /dev/null +++ b/pom.xml @@ -0,0 +1,139 @@ + + + + 4.0.0 + + xyz.valnet.hadean + hadean + 1.0-SNAPSHOT + + hadean + + http://www.example.com + + + UTF-8 + 17 + 17 + 3.3.1 + natives-windows + + + + + + + org.lwjgl + lwjgl-bom + ${lwjgl.version} + import + pom + + + + + + + + org.lwjgl + lwjgl + + + org.lwjgl + lwjgl-assimp + + + org.lwjgl + lwjgl-glfw + + + org.lwjgl + lwjgl-openal + + + org.lwjgl + lwjgl-opengl + + + org.lwjgl + lwjgl-stb + + + org.lwjgl + lwjgl + ${lwjgl.natives} + + + org.lwjgl + lwjgl-assimp + ${lwjgl.natives} + + + org.lwjgl + lwjgl-glfw + ${lwjgl.natives} + + + org.lwjgl + lwjgl-openal + ${lwjgl.natives} + + + org.lwjgl + lwjgl-opengl + ${lwjgl.natives} + + + org.lwjgl + lwjgl-stb + ${lwjgl.natives} + + + + + + + + + maven-clean-plugin + 3.1.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + + + maven-surefire-plugin + 2.22.1 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + + diff --git a/res/textures.pdn b/res/textures.pdn new file mode 100644 index 0000000..5447fc7 Binary files /dev/null and b/res/textures.pdn differ diff --git a/res/textures.png b/res/textures.png new file mode 100644 index 0000000..052e2df Binary files /dev/null and b/res/textures.png differ diff --git a/shaders/flat.frag b/shaders/flat.frag new file mode 100644 index 0000000..3346114 --- /dev/null +++ b/shaders/flat.frag @@ -0,0 +1,15 @@ +//SpriteBatch will use texture unit 0 +uniform sampler2D u_texture; + +//"in" varyings from our vertex shader +varying vec4 vColor; +varying vec2 vTexCoord; + +void main() { + vec4 texColor = texture2D(u_texture, vTexCoord); + if(texColor == vec4(1, 0, 1, 1) || texColor == vec4(1, 0, 0, 1)) { + gl_FragColor = vec4(0, 0, 0, 0); + } else { + gl_FragColor = texColor * vColor; + } +} \ No newline at end of file diff --git a/shaders/flat.vert b/shaders/flat.vert new file mode 100644 index 0000000..e9a2591 --- /dev/null +++ b/shaders/flat.vert @@ -0,0 +1,17 @@ +//combined projection and view matrix +uniform mat4 uProjection; +uniform vec4 uColor; + +//"in" attributes from our SpriteBatch +attribute vec2 Position; +attribute vec2 TexCoord; + +//"out" varyings to our fragment shader +varying vec4 vColor; +varying vec2 vTexCoord; + +void main() { + vColor = uColor; + vTexCoord = TexCoord; + gl_Position = uProjection * vec4(Position, 0.0, 1.0); +} \ No newline at end of file diff --git a/src/main/java/xyz/valnet/engine/App.java b/src/main/java/xyz/valnet/engine/App.java new file mode 100644 index 0000000..5f5f6ce --- /dev/null +++ b/src/main/java/xyz/valnet/engine/App.java @@ -0,0 +1,149 @@ +package xyz.valnet.engine; + +import org.lwjgl.*; +import org.lwjgl.glfw.*; +import org.lwjgl.opengl.*; +import org.lwjgl.system.*; + +import xyz.valnet.engine.math.Matrix4f; + +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.*; + +public class App { + + // The window handle + private long window; + private int width = 800, height = 450; + private Matrix4f matrix = Matrix4f.orthographic(0, width, height, 0, 1, -1); + public static int mouseX, mouseY; + public static boolean mouseLeft, mouseMiddle, mouseRight; + + private Game game; + + public void run() { + System.out.println("Hello 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(); + } + + 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(width, height, "Hello World!", NULL, NULL); + if ( window == NULL ) + throw new RuntimeException("Failed to create the GLFW window"); + + // Setup 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 + }); + + + glfwSetCursorPosCallback(window, new GLFWCursorPosCallback() { + @Override + public void invoke(long window, double xpos, double ypos) { + mouseX = (int) xpos; + mouseY = (int) ypos; + } + }); + + glfwSetMouseButtonCallback(window, new GLFWMouseButtonCallback() { + @Override + public void invoke(long window, int button, int action, int mods) { + + if(button >= 3) return; + // System.out.println("window: " + window + ", button: " + button + ", action: " + action + ", mods: " + mods); + if(button == GLFW_MOUSE_BUTTON_LEFT) { mouseLeft = action == 1; return; } + if(button == GLFW_MOUSE_BUTTON_RIGHT) { mouseRight = action == 1; return; } + if(button == GLFW_MOUSE_BUTTON_MIDDLE) { mouseMiddle = action == 1; return ; } + + } + }); + + // 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 + ); + } // the stack frame is popped automatically + + // Make the OpenGL context current + glfwMakeContextCurrent(window); + // Enable v-sync + glfwSwapInterval(1); + + // Make the window visible + glfwShowWindow(window); + + // 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(); + + + glClearColor(0.3f, 0.3f, 0.3f, 1.0f); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + game.start(); + } + + private void loop() { + while (!glfwWindowShouldClose(window)) { + game.updateViewMatrix(matrix); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer + + game.render(); + game.update(); + + glfwSwapBuffers(window); + glfwPollEvents(); + } + } + + public App(Game game) { + this.game = game; + } +} \ No newline at end of file diff --git a/src/main/java/xyz/valnet/engine/Game.java b/src/main/java/xyz/valnet/engine/Game.java new file mode 100644 index 0000000..21d5163 --- /dev/null +++ b/src/main/java/xyz/valnet/engine/Game.java @@ -0,0 +1,50 @@ +package xyz.valnet.engine; + +import static xyz.valnet.engine.util.Math.lerp; + +import xyz.valnet.engine.math.Matrix4f; +import xyz.valnet.engine.scenegraph.IScene; + +public abstract class Game { + private IScene scene; + + protected float averageFPS = 0; + protected int measuredFPS = 0; + + private int framesSinceKeyframe = 0; + private long lastFrame = System.nanoTime(); + private long lastKeyframe = System.nanoTime(); + + public abstract void start(); + + public void changeScene(IScene scene) { + if(this.scene != null) { + this.scene.disable(); + } + scene.enable(); + this.scene = scene; + } + + public void render() { + scene.render(); + } + + public void update() { + scene.update(0); + + long nanoTime = System.nanoTime(); + + // average framerate + averageFPS = lerp(averageFPS, 1_000_000_000f / (nanoTime - lastFrame), (nanoTime - lastFrame) / 1_000_000_000f); + lastFrame = nanoTime; + + framesSinceKeyframe ++; + if(nanoTime > lastKeyframe + 1_000_000_000) { + measuredFPS = framesSinceKeyframe; + framesSinceKeyframe = 0; + lastKeyframe += 1_000_000_000; + } + } + + public abstract void updateViewMatrix(Matrix4f matrix); +} diff --git a/src/main/java/xyz/valnet/engine/graphics/Drawing.java b/src/main/java/xyz/valnet/engine/graphics/Drawing.java new file mode 100644 index 0000000..77ff94b --- /dev/null +++ b/src/main/java/xyz/valnet/engine/graphics/Drawing.java @@ -0,0 +1,36 @@ +package xyz.valnet.engine.graphics; + +import static org.lwjgl.opengl.GL20.*; + +import xyz.valnet.engine.shaders.SimpleShader; + +public class Drawing { + + private static Texture bound = null; + + public static void drawSprite(Sprite sprite, int x, int y) { + drawSprite(sprite, x, y, sprite.width, sprite.height); + } + + public static void drawSprite(Sprite sprite, int x, int y, int width, int height) { + // lazy texture binding + if(bound != sprite.atlas) { + if(bound != null) bound.unbind(); + sprite.atlas.bind(); + } + + glBegin(GL_QUADS); + glVertexAttrib2f(SimpleShader.TEX_COORD, sprite.sourceBoxUV.x, sprite.sourceBoxUV.y); + glVertex2f(x, y); + + glVertexAttrib2f(SimpleShader.TEX_COORD, sprite.sourceBoxUV.x + sprite.sourceBoxUV.z, sprite.sourceBoxUV.y); + glVertex2f(x + width, y); + + glVertexAttrib2f(SimpleShader.TEX_COORD, sprite.sourceBoxUV.x + sprite.sourceBoxUV.z, sprite.sourceBoxUV.y + sprite.sourceBoxUV.w); + glVertex2f(x + width, y + height); + + glVertexAttrib2f(SimpleShader.TEX_COORD, sprite.sourceBoxUV.x, sprite.sourceBoxUV.y + sprite.sourceBoxUV.w); + glVertex2f(x, y + height); + glEnd(); + } +} \ No newline at end of file diff --git a/src/main/java/xyz/valnet/engine/graphics/Font.java b/src/main/java/xyz/valnet/engine/graphics/Font.java new file mode 100644 index 0000000..a794f71 --- /dev/null +++ b/src/main/java/xyz/valnet/engine/graphics/Font.java @@ -0,0 +1,55 @@ +package xyz.valnet.engine.graphics; + +import java.util.Map; + +import xyz.valnet.engine.math.Vector4i; + +public class Font { + + private Map charset; + private final int w, h; + + public Font(Map charset, int w, int h) { + this.charset = charset; + this.w = w; + this.h = h; + } + + public void drawString(String str, int x, int y) { + int cursorX = x; + int cursorY = y; + + Sprite s; + + for(char c : str.toCharArray()) { + if(c == '\n') { + cursorY += h; + cursorX = x; + continue; + } + if(c == '\r') continue; + if(c == '\t') { + cursorX += w * 4; + continue; + } + if(c == ' ' || !charset.containsKey(c)) { + cursorX += w; + continue; + } + s = charset.get(c); + Drawing.drawSprite(s, cursorX, cursorY); + cursorX += w; + } + } + + public Vector4i measure(String text) { + String[] lines = text.split("\n"); + int longest = 0; + int c = 0; + for(String line : lines) { + c = line.length(); + if(c > longest) longest = c; + } + return new Vector4i(longest * w, lines.length * h, 0, 0); + } +} diff --git a/src/main/java/xyz/valnet/engine/graphics/Sprite.java b/src/main/java/xyz/valnet/engine/graphics/Sprite.java new file mode 100644 index 0000000..5d8cf36 --- /dev/null +++ b/src/main/java/xyz/valnet/engine/graphics/Sprite.java @@ -0,0 +1,27 @@ +package xyz.valnet.engine.graphics; + +import xyz.valnet.engine.math.Vector4f; +import xyz.valnet.engine.math.Vector4i; + +public class Sprite { + public final Vector4f sourceBoxUV; + private final Vector4i sourceBoxPixels; + public final Texture atlas; + public final int width; + public final int height; + + public Sprite(Texture tex, Vector4i box) { + sourceBoxPixels = box; + sourceBoxUV = new Vector4f( + sourceBoxPixels.x / (float) tex.width, + sourceBoxPixels.y / (float) tex.height, + sourceBoxPixels.z / (float) tex.width, + sourceBoxPixels.w / (float) tex.height + ); + atlas = tex; + width = sourceBoxPixels.z; + height = sourceBoxPixels.w; + } + + +} diff --git a/src/main/java/xyz/valnet/engine/graphics/Texture.java b/src/main/java/xyz/valnet/engine/graphics/Texture.java new file mode 100644 index 0000000..580b0b6 --- /dev/null +++ b/src/main/java/xyz/valnet/engine/graphics/Texture.java @@ -0,0 +1,82 @@ +package xyz.valnet.engine.graphics; + +import java.awt.image.BufferedImage; +import java.io.FileInputStream; +import java.io.IOException; + +import javax.imageio.ImageIO; + +import xyz.valnet.engine.util.BufferUtils; + +import static org.lwjgl.opengl.GL20.*; + +public class Texture { + + public final int width, height; + public final int handle; + private final BufferedImage img; + + private boolean registered = false; + + public Texture(String path) { + img = loadImageFromDisk(path); + if(img == null) { + width = 0; + height = 0; + handle = -1; + return; + } + width = img.getWidth(); + height = img.getHeight(); + handle = registerImg(); + } + + private BufferedImage loadImageFromDisk (String path) { + try { + return ImageIO.read(new FileInputStream(path)); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + private int registerImg() { + if(registered) return handle; + registered = true; + + int[] pixels = new int[width * height]; + int[] data = new int[width * height]; + + // populate pixels + img.getRGB(0, 0, width, height, pixels, 0, width); + + // convert to data + for (int i = 0; i < width * height; i++) { + int a = (pixels[i] & 0xff000000) >> 24; + int r = (pixels[i] & 0xff0000) >> 16; + int g = (pixels[i] & 0xff00) >> 8; + int b = (pixels[i] & 0xff); + + data[i] = a << 24 | b << 16 | g << 8 | r; + } + + int result = glGenTextures(); + glBindTexture(GL_TEXTURE_2D, result); + glActiveTexture(GL_TEXTURE0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, BufferUtils.createIntBuffer(data)); + glBindTexture(GL_TEXTURE_2D, 0); + return result; + // return 0; + } + + public void bind() { + glBindTexture(GL_TEXTURE_2D, handle); + } + + public void unbind() { + glBindTexture(GL_TEXTURE_2D, 0); + } + +} diff --git a/src/main/java/xyz/valnet/engine/graphics/Tile9.java b/src/main/java/xyz/valnet/engine/graphics/Tile9.java new file mode 100644 index 0000000..56987d9 --- /dev/null +++ b/src/main/java/xyz/valnet/engine/graphics/Tile9.java @@ -0,0 +1,49 @@ +package xyz.valnet.engine.graphics; + +public class Tile9 { + + private final Sprite topLeft; + private final Sprite top; + private final Sprite topRight; + private final Sprite left; + private final Sprite center; + private final Sprite right; + private final Sprite bottomLeft; + private final Sprite bottom; + private final Sprite bottomRight; + + public Tile9( + Sprite topLeft, + Sprite top, + Sprite topRight, + Sprite left, + Sprite center, + Sprite right, + Sprite bottomLeft, + Sprite bottom, + Sprite bottomRight + ) { + this.topLeft = topLeft; + this.top = top; + this.topRight = topRight; + this.left = left; + this.center = center; + this.right = right; + this.bottomLeft = bottomLeft; + this.bottom = bottom; + this.bottomRight = bottomRight; + } + + public void draw(int x, int y, int w, int h) { + Drawing.drawSprite(topLeft, x, y, topLeft.width, topLeft.height); + Drawing.drawSprite(top, x + topLeft.width, y, w - topLeft.width - topRight.width, top.height); + Drawing.drawSprite(topRight, x + w - topRight.width, y, topLeft.width, topLeft.height); + Drawing.drawSprite(left, x, y + topLeft.height, left.width, h - top.height - bottom.height); + Drawing.drawSprite(center, x + left.width, y + top.height, w - left.width - right.width, h - top.height - bottom.height); + Drawing.drawSprite(right, x + w - right.width, y + topRight.height, right.width, h - top.height - bottom.height); + Drawing.drawSprite(bottomLeft, x, y + h - bottomLeft.height, bottomLeft.width, bottomLeft.height); + Drawing.drawSprite(bottom, x + bottomLeft.width, y + h - bottom.height, w - bottomLeft.width - bottomRight.width, bottom.height); + Drawing.drawSprite(bottomRight, x + w - bottomRight.width, y + h - bottomRight.height, bottomLeft.width, bottomLeft.height); + } + +} diff --git a/src/main/java/xyz/valnet/engine/math/Matrix4f.java b/src/main/java/xyz/valnet/engine/math/Matrix4f.java new file mode 100644 index 0000000..e37ea57 --- /dev/null +++ b/src/main/java/xyz/valnet/engine/math/Matrix4f.java @@ -0,0 +1,86 @@ +package xyz.valnet.engine.math; + +import java.nio.FloatBuffer; + +import xyz.valnet.engine.util.BufferUtils; + +public class Matrix4f { + + public static final int SIZE = 4 * 4; + public float[] elements = new float[SIZE]; + + public Matrix4f() { + + } + + public static Matrix4f identity() { + Matrix4f result = new Matrix4f(); + for (int i = 0; i < SIZE; i++) { + result.elements[i] = 0.0f; + } + result.elements[0 + 0 * 4] = 1.0f; + result.elements[1 + 1 * 4] = 1.0f; + result.elements[2 + 2 * 4] = 1.0f; + result.elements[3 + 3 * 4] = 1.0f; + + return result; + } + + public static Matrix4f orthographic(float left, float right, float bottom, float top, float near, float far) { + Matrix4f result = identity(); + + result.elements[0 + 0 * 4] = 2.0f / (right - left); + + result.elements[1 + 1 * 4] = 2.0f / (top - bottom); + + result.elements[2 + 2 * 4] = 2.0f / (near - far); + + result.elements[0 + 3 * 4] = (left + right) / (left - right); + result.elements[1 + 3 * 4] = (bottom + top) / (bottom - top); + result.elements[2 + 3 * 4] = (far + near) / (far - near); + + return result; + } + + public static Matrix4f translate(Vector3f vector) { + Matrix4f result = identity(); + result.elements[0 + 3 * 4] = vector.x; + result.elements[1 + 3 * 4] = vector.y; + result.elements[2 + 3 * 4] = vector.z; + return result; + } + + public static Matrix4f rotate(float angle) { + Matrix4f result = identity(); + float r = (float) Math.toRadians(angle); + float cos = (float) Math.cos(r); + float sin = (float) Math.sin(r); + + result.elements[0 + 0 * 4] = cos; + result.elements[1 + 0 * 4] = sin; + + result.elements[0 + 1 * 4] = -sin; + result.elements[1 + 1 * 4] = cos; + + return result; + } + + public Matrix4f multiply(Matrix4f matrix) { + Matrix4f result = new Matrix4f(); + for (int y = 0; y < 4; y++) { + for (int x = 0; x < 4; x++) { + float sum = 0.0f; + for (int e = 0; e < 4; e++) { + sum += this.elements[x + e * 4] * matrix.elements[e + y * 4]; + } + result.elements[x + y * 4] = sum; + } + } + return result; + } + + public FloatBuffer toFloatBuffer() { + return BufferUtils.createFloatBuffer(elements); + } + +} diff --git a/src/main/java/xyz/valnet/engine/math/Vector3f.java b/src/main/java/xyz/valnet/engine/math/Vector3f.java new file mode 100644 index 0000000..3f15490 --- /dev/null +++ b/src/main/java/xyz/valnet/engine/math/Vector3f.java @@ -0,0 +1,19 @@ +package xyz.valnet.engine.math; + +public class Vector3f { + + public float x, y, z; + + public Vector3f() { + x = 0.0f; + y = 0.0f; + z = 0.0f; + } + + public Vector3f(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + } + +} diff --git a/src/main/java/xyz/valnet/engine/math/Vector4f.java b/src/main/java/xyz/valnet/engine/math/Vector4f.java new file mode 100644 index 0000000..6e7d28f --- /dev/null +++ b/src/main/java/xyz/valnet/engine/math/Vector4f.java @@ -0,0 +1,29 @@ +package xyz.valnet.engine.math; + +public class Vector4f { + + public float x, y, z, w; + + public Vector4f() { + x = 0.0f; + y = 0.0f; + z = 0.0f; + w = 0.0f; + } + + public Vector4f(float x, float y, float z, float w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + public static Vector4f one = new Vector4f(1f, 1f, 1f, 1f); + public static Vector4f black = new Vector4f(0f, 0f, 0f, 1f); + public static Vector4f zero = new Vector4f(0f, 0f, 0f, 0f); + + public String toString() { + return "(" + this.x + ", " + this.y + ", " + this.z + ", " + this.w + ")"; + } + +} diff --git a/src/main/java/xyz/valnet/engine/math/Vector4i.java b/src/main/java/xyz/valnet/engine/math/Vector4i.java new file mode 100644 index 0000000..3fc72fb --- /dev/null +++ b/src/main/java/xyz/valnet/engine/math/Vector4i.java @@ -0,0 +1,21 @@ +package xyz.valnet.engine.math; + +public class Vector4i { + + public int x, y, z, w; + + public Vector4i() { + x = 0; + y = 0; + z = 0; + w = 0; + } + + public Vector4i(int x, int y, int z, int w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + +} diff --git a/src/main/java/xyz/valnet/engine/scenegraph/GameObject.java b/src/main/java/xyz/valnet/engine/scenegraph/GameObject.java new file mode 100644 index 0000000..ea51b4c --- /dev/null +++ b/src/main/java/xyz/valnet/engine/scenegraph/GameObject.java @@ -0,0 +1,15 @@ +package xyz.valnet.engine.scenegraph; + +public class GameObject implements IRenderable, ITickable { + // private IScene scene; + + public GameObject(IScene scene) { + // this.scene = scene; + } + + @Override + public void render() {} + + @Override + public void tick(float dTime) {} +} diff --git a/src/main/java/xyz/valnet/engine/scenegraph/IRenderable.java b/src/main/java/xyz/valnet/engine/scenegraph/IRenderable.java new file mode 100644 index 0000000..0a39453 --- /dev/null +++ b/src/main/java/xyz/valnet/engine/scenegraph/IRenderable.java @@ -0,0 +1,5 @@ +package xyz.valnet.engine.scenegraph; + +public interface IRenderable { + public void render(); +} diff --git a/src/main/java/xyz/valnet/engine/scenegraph/IRenderableListener.java b/src/main/java/xyz/valnet/engine/scenegraph/IRenderableListener.java new file mode 100644 index 0000000..8f4a1ec --- /dev/null +++ b/src/main/java/xyz/valnet/engine/scenegraph/IRenderableListener.java @@ -0,0 +1,6 @@ +package xyz.valnet.engine.scenegraph; + +public interface IRenderableListener { + public void addRenderable(IRenderable renderable); + public void removeRenderable(IRenderable renderable); +} diff --git a/src/main/java/xyz/valnet/engine/scenegraph/IScene.java b/src/main/java/xyz/valnet/engine/scenegraph/IScene.java new file mode 100644 index 0000000..cd61300 --- /dev/null +++ b/src/main/java/xyz/valnet/engine/scenegraph/IScene.java @@ -0,0 +1,9 @@ +package xyz.valnet.engine.scenegraph; + +public interface IScene { + public void render(); + public void update(float dTime); + + public void enable(); + public void disable(); +} diff --git a/src/main/java/xyz/valnet/engine/scenegraph/ITickable.java b/src/main/java/xyz/valnet/engine/scenegraph/ITickable.java new file mode 100644 index 0000000..9af77f9 --- /dev/null +++ b/src/main/java/xyz/valnet/engine/scenegraph/ITickable.java @@ -0,0 +1,5 @@ +package xyz.valnet.engine.scenegraph; + +public interface ITickable { + public void tick(float dTime); +} diff --git a/src/main/java/xyz/valnet/engine/shaders/Shader.java b/src/main/java/xyz/valnet/engine/shaders/Shader.java new file mode 100644 index 0000000..377d2c9 --- /dev/null +++ b/src/main/java/xyz/valnet/engine/shaders/Shader.java @@ -0,0 +1,145 @@ +package xyz.valnet.engine.shaders; + +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL20.*; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import xyz.valnet.engine.math.Matrix4f; +import xyz.valnet.engine.math.Vector3f; +import xyz.valnet.engine.math.Vector4f; + +public class Shader { + + private boolean enabled = false; + + public final int handle; + + public final static int POSITION = 0; + + private Map locationCache = new HashMap(); + + public Shader(String vertPath, String fragPath) { + handle = load(vertPath, fragPath); + } + + private static String loadAsString(String file) { + StringBuilder result = new StringBuilder(); + try { + BufferedReader reader = new BufferedReader(new FileReader(file)); + String buffer = ""; + while ((buffer = reader.readLine()) != null) { + result.append(buffer + '\n'); + } + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + return result.toString(); + } + + public int load(String vertPath, String fragPath) { + String vert = Shader.loadAsString(vertPath); + String frag = Shader.loadAsString(fragPath); + return create(vert, frag); + } + + public int create(String vert, String frag) { + int program = glCreateProgram(); + int vertID = glCreateShader(GL_VERTEX_SHADER); + int fragID = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(vertID, vert); + glShaderSource(fragID, frag); + + glCompileShader(vertID); + if (glGetShaderi(vertID, GL_COMPILE_STATUS) == GL_FALSE) { + System.err.println("Failed to compile vertex shader!"); + System.err.println(glGetShaderInfoLog(vertID)); + return -1; + } + + glCompileShader(fragID); + if (glGetShaderi(fragID, GL_COMPILE_STATUS) == GL_FALSE) { + System.err.println("Failed to compile fragment shader!"); + System.err.println(glGetShaderInfoLog(fragID)); + return -1; + } + + bindAttributes(program); + + glAttachShader(program, vertID); + glAttachShader(program, fragID); + glLinkProgram(program); + glValidateProgram(program); + + glDeleteShader(vertID); + glDeleteShader(fragID); + + return program; + } + + protected void bindAttributes(int program) { + glBindAttribLocation(program, POSITION, "Position"); + } + + public int getUniform(String name) { + if (locationCache.containsKey(name)) + return locationCache.get(name); + + int result = glGetUniformLocation(handle, name); + if (result == -1) + System.err.println("Could not find uniform variable '" + name + "'!"); + else + locationCache.put(name, result); + return result; + } + + public void setUniform1i(String name, int value) { + if (!enabled) enable(); + glUniform1i(getUniform(name), value); + } + + public void setUniform1f(String name, float value) { + if (!enabled) enable(); + glUniform1f(getUniform(name), value); + } + + public void setUniform2f(String name, float x, float y) { + if (!enabled) enable(); + glUniform2f(getUniform(name), x, y); + } + + public void setUniform3f(String name, Vector3f vector) { + if (!enabled) enable(); + glUniform3f(getUniform(name), vector.x, vector.y, vector.z); + } + + public void setUniform4f(String name, Vector4f vector) { + if (!enabled) enable(); + glUniform4f(getUniform(name), vector.x, vector.y, vector.z, vector.w); + } + + public void setUniformMat4f(String name, Matrix4f matrix) { + if (!enabled) enable(); + glUniformMatrix4fv(getUniform(name), false, matrix.toFloatBuffer()); + } + + public void setMatrices (Matrix4f projection) { + setUniformMat4f("uProjection", projection); + } + + public void enable() { + glUseProgram(handle); + enabled = true; + } + + public void disable() { + glUseProgram(0); + enabled = false; + } + +} diff --git a/src/main/java/xyz/valnet/engine/shaders/SimpleShader.java b/src/main/java/xyz/valnet/engine/shaders/SimpleShader.java new file mode 100644 index 0000000..dea30de --- /dev/null +++ b/src/main/java/xyz/valnet/engine/shaders/SimpleShader.java @@ -0,0 +1,59 @@ +package xyz.valnet.engine.shaders; + +import java.util.Stack; + +import static org.lwjgl.opengl.GL20.*; + +import xyz.valnet.engine.math.Vector4f; + +public class SimpleShader extends Shader { + + private Stack colorStack = new Stack(); + + public final static int COLOR = 1; + public final static int TEX_COORD = 2; + + public SimpleShader(String vertPath, String fragPath) { + super(vertPath, fragPath); + } + + public void pushColor(Vector4f color) { + // System.out.println("pushing " + color + " onto color stack"); + colorStack.push(color); + // printColorStack(); + + setUniform4f("uColor", color); + } + + public void swapColor(Vector4f color) { + popColor(); + pushColor(color); + } + + public void popColor() { + // System.out.println("popping the color stack"); + colorStack.pop(); + Vector4f newColor = colorStack.peek(); + // printColorStack(); + + if(newColor == null) { + setUniform4f("uColor", Vector4f.one); + return; + } + setUniform4f("uColor", newColor); + } + + // private void printColorStack() { + // for(Vector4f color : colorStack) { + // System.out.println(" " + color); + // } + // System.out.println(""); + // } + + @Override + protected void bindAttributes(int program) { + glBindAttribLocation(program, COLOR, "Color"); + glBindAttribLocation(program, TEX_COORD, "TexCoord"); + } + +} diff --git a/src/main/java/xyz/valnet/engine/util/BufferUtils.java b/src/main/java/xyz/valnet/engine/util/BufferUtils.java new file mode 100644 index 0000000..d1ad7e2 --- /dev/null +++ b/src/main/java/xyz/valnet/engine/util/BufferUtils.java @@ -0,0 +1,31 @@ +package xyz.valnet.engine.util; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +public class BufferUtils { + + private BufferUtils() { + } + + public static ByteBuffer createByteBuffer(byte[] array) { + ByteBuffer result = ByteBuffer.allocateDirect(array.length).order(ByteOrder.nativeOrder()); + result.put(array).flip(); + return result; + } + + public static FloatBuffer createFloatBuffer(float[] array) { + FloatBuffer result = ByteBuffer.allocateDirect(array.length << 2).order(ByteOrder.nativeOrder()).asFloatBuffer(); + result.put(array).flip(); + return result; + } + + public static IntBuffer createIntBuffer(int[] array) { + IntBuffer result = ByteBuffer.allocateDirect(array.length << 2).order(ByteOrder.nativeOrder()).asIntBuffer(); + result.put(array).flip(); + return result; + } + +} diff --git a/src/main/java/xyz/valnet/engine/util/Math.java b/src/main/java/xyz/valnet/engine/util/Math.java new file mode 100644 index 0000000..4cbd5da --- /dev/null +++ b/src/main/java/xyz/valnet/engine/util/Math.java @@ -0,0 +1,9 @@ +package xyz.valnet.engine.util; + +public class Math { + public static float lerp(float a, float b, float n) { + if(n >= 1) return b; + if(n <= 0) return a; + return a + (b - a) * n; + } +} diff --git a/src/main/java/xyz/valnet/hadean/HadeanGame.java b/src/main/java/xyz/valnet/hadean/HadeanGame.java new file mode 100644 index 0000000..8d8cce3 --- /dev/null +++ b/src/main/java/xyz/valnet/hadean/HadeanGame.java @@ -0,0 +1,63 @@ +package xyz.valnet.hadean; + +import xyz.valnet.engine.App; +import xyz.valnet.engine.Game; +import xyz.valnet.engine.math.Matrix4f; +import xyz.valnet.engine.math.Vector4f; + +import xyz.valnet.hadean.scenes.MenuScene; +import xyz.valnet.hadean.util.Assets; + + +public class HadeanGame extends Game { + public static final HadeanGame Hadean = new HadeanGame(); + + public static void main(String[] args) { + new App(Hadean).run(); + } + + @Override + public void start() { + Assets.flat.pushColor(Vector4f.one); + changeScene(new MenuScene()); + } + + @Override + public void render() { + super.render(); + renderDebugInfo(); + } + + private Runtime runtime = Runtime.getRuntime(); + private static Vector4f fontColor = new Vector4f(0, 1, 1, 1); + + private void renderDebugInfo() { + long allocated = runtime.totalMemory(); + long max = runtime.maxMemory(); + + Assets.flat.pushColor(Vector4f.black); + Assets.font.drawString("FPS: " + Math.round(averageFPS) + "/" + measuredFPS + " | AVG/MEASURED", 1, 1); + Assets.font.drawString("Mouse: <" + App.mouseX + ", " + App.mouseY + ">", 1, 17); + Assets.font.drawString("MEMORY: " + (int)((allocated / (double)max) * 100) + "% (" + (allocated / (1024 * 1024)) + "/" + (max / (1024 * 1024)) + "MB)", 1, 33); + Assets.font.drawString("", 1, 49); + Assets.font.drawString("", 1, 65); + Assets.font.drawString("", 1, 81); + + Assets.flat.swapColor(fontColor); + Assets.font.drawString("FPS: " + Math.round(averageFPS) + "/" + measuredFPS + " | AVG/MEASURED", 0, 0); + Assets.font.drawString("Mouse: <" + App.mouseX + ", " + App.mouseY + ">", 0, 16); + Assets.font.drawString("MEMORY: " + (int)((allocated / (double)max) * 100) + "% (" + (allocated / (1024 * 1024)) + "/" + (max / (1024 * 1024)) + "MB)", 0, 32); + Assets.font.drawString("", 0, 48); + Assets.font.drawString("", 0, 64); + Assets.font.drawString("", 0, 80); + + Assets.flat.popColor(); + } + + // receive the updated matrix every frame for the actual window. + @Override + public void updateViewMatrix(Matrix4f matrix) { + Assets.flat.setMatrices(matrix); + } + +} diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/Terrain.java b/src/main/java/xyz/valnet/hadean/gameobjects/Terrain.java new file mode 100644 index 0000000..fc81051 --- /dev/null +++ b/src/main/java/xyz/valnet/hadean/gameobjects/Terrain.java @@ -0,0 +1,47 @@ +package xyz.valnet.hadean.gameobjects; + +import xyz.valnet.engine.graphics.Drawing; +import xyz.valnet.engine.graphics.Sprite; +import xyz.valnet.engine.math.Vector4f; +import xyz.valnet.engine.scenegraph.GameObject; +import xyz.valnet.engine.scenegraph.IScene; +import xyz.valnet.hadean.util.Assets; + +public class Terrain extends GameObject { + + private final int WORLD_SIZE = 30; + private final int TILE_SIZE = 8; + + private int[][] variation = new int[WORLD_SIZE][WORLD_SIZE]; + private Vector4f[][] colorVariations = new Vector4f[WORLD_SIZE][WORLD_SIZE]; + + public Terrain(IScene scene) { + super(scene); + for (int i = 0; i < WORLD_SIZE; i++) { + for (int j = 0; j < WORLD_SIZE; j++) { + variation[i][j] = (int) Math.floor(Math.random() * 4); + colorVariations[i][j] = new Vector4f((float) Math.random() * 0.2f, 0.4f + (float) Math.random() * 0.2f, (float) Math.random() * 0.05f, 1f); + } + } + } + + @Override + public void render() { + int left = 400 - (WORLD_SIZE * TILE_SIZE / 2); + int top = 225 - (WORLD_SIZE * TILE_SIZE / 2); + + + Sprite s; + Assets.flat.pushColor(Vector4f.one); + for (int i = 0; i < WORLD_SIZE; i++) { + for (int j = 0; j < WORLD_SIZE; j++) { + s = Assets.defaultTerrain[variation[i][j]]; + Assets.flat.swapColor(colorVariations[i][j]); + Drawing.drawSprite(s, left + i * s.width, top + j * s.height); + } + } + Assets.flat.popColor(); + + } + +} diff --git a/src/main/java/xyz/valnet/hadean/input/Button.java b/src/main/java/xyz/valnet/hadean/input/Button.java new file mode 100644 index 0000000..2e31b59 --- /dev/null +++ b/src/main/java/xyz/valnet/hadean/input/Button.java @@ -0,0 +1,121 @@ +package xyz.valnet.hadean.input; + +import static xyz.valnet.engine.util.Math.lerp; + +import xyz.valnet.engine.App; +import xyz.valnet.engine.graphics.Tile9; +import xyz.valnet.engine.math.Vector4f; +import xyz.valnet.engine.math.Vector4i; +import xyz.valnet.hadean.util.Assets; + +public class Button { + + private final int x, y, width, height; + private final String text; + private final Tile9 frame; + private final int textWidth, textHeight; + private float hPad, vPad; + private Vector4i box; + + protected float hoverVPad = 0.0f; + protected float hoverHPad = 0.1f; + protected float activeVPad = 0.1f; + protected float activeHPad = 0.0f; + + public Button(Tile9 frame, String text, int x, int y, int w, int h) { + this.x = x; + this.y = y; + width = w; + height = h; + this.text = text; + this.frame = frame; + Vector4i measuredText = Assets.font.measure(text); + textWidth = measuredText.x; + textHeight = measuredText.y; + box = new Vector4i(x, y, w, h); + } + + public void draw() { + frame.draw(box.x, box.y, box.z, box.w); + + Assets.flat.pushColor(Vector4f.black); + Assets.font.drawString(text, 1 + x + (width - textWidth) / 2, 1 + y + (height - textHeight) / 2); + + Assets.flat.swapColor(Vector4f.one); + Assets.font.drawString(text, x + (width - textWidth) / 2, y + (height - textHeight) / 2); + + Assets.flat.popColor(); + } + + private boolean hovered = false; + + private int state = 0; + private final static int IDLE = 0; + private final static int INACTIVE = 1; + private final static int HOVER = 2; + private final static int ACTIVE = 3; + private final static int ACTIVE_NO_HOVER = 4; + + private IButtonListener listener = null; + + public void update() { + box.x = x - (int)hPad; + box.y = y - (int)vPad; + box.z = width + ((int)hPad) * 2; + box.w = height + ((int)vPad) * 2; + + hovered = App.mouseX >= box.x && App.mouseX <= box.x + box.z && App.mouseY >= box.y && App.mouseY <= box.y + box.w; + boolean mouseDown = App.mouseLeft; + + float desiredVPad = 0, desiredHPad = 0; + + if(state == HOVER) { + desiredVPad += height * hoverVPad; + desiredHPad += width * hoverHPad; + } + + if(state == ACTIVE) { + desiredVPad += height * activeVPad; + desiredHPad += width * activeHPad; + } + + vPad = lerp(vPad, desiredVPad, 0.1f); + hPad = lerp(hPad, desiredHPad, 0.1f); + + if(state == IDLE) { + if(hovered) { + state = HOVER; + } else if (mouseDown) { + state = INACTIVE; + } + } else if (state == HOVER) { + if(!hovered) { + state = IDLE; + } else if(mouseDown) { + state = ACTIVE; + } + } else if (state == INACTIVE) { + if(!mouseDown) { + state = IDLE; + } + } else if (state == ACTIVE) { + if(!hovered) { + state = ACTIVE_NO_HOVER; + } else if(!mouseDown) { + state = IDLE; + listener.click(this); + } + } else if (state == ACTIVE_NO_HOVER) { + if(hovered) { + state = ACTIVE; + } else if (!mouseDown) { + state = IDLE; + } + } + + } + + public void registerClickListener(IButtonListener listener) { + this.listener = listener; + } +} diff --git a/src/main/java/xyz/valnet/hadean/input/IButtonListener.java b/src/main/java/xyz/valnet/hadean/input/IButtonListener.java new file mode 100644 index 0000000..b3469b9 --- /dev/null +++ b/src/main/java/xyz/valnet/hadean/input/IButtonListener.java @@ -0,0 +1,5 @@ +package xyz.valnet.hadean.input; + +public interface IButtonListener { + public void click(Button target); +} \ No newline at end of file diff --git a/src/main/java/xyz/valnet/hadean/scenes/GameScene.java b/src/main/java/xyz/valnet/hadean/scenes/GameScene.java new file mode 100644 index 0000000..74dcf8c --- /dev/null +++ b/src/main/java/xyz/valnet/hadean/scenes/GameScene.java @@ -0,0 +1,44 @@ +package xyz.valnet.hadean.scenes; + +import java.util.ArrayList; +import java.util.List; + +import xyz.valnet.engine.scenegraph.GameObject; +import xyz.valnet.engine.scenegraph.IScene; +import xyz.valnet.hadean.gameobjects.Terrain; + +public class GameScene implements IScene { + + // generic + private List objects = new ArrayList(); + // private List renderables = new ArrayList(); + + // specific + private GameObject terrain; + + @Override + public void render() { + for(GameObject obj : objects) { + ((Terrain)obj).render(); + } + } + + @Override + public void update(float dTime) { + for(GameObject obj : objects) { + obj.tick(dTime); + } + } + + @Override + public void enable() { + terrain = new Terrain(this); + objects.add(terrain); + } + + @Override + public void disable() { + objects.clear(); + } + +} diff --git a/src/main/java/xyz/valnet/hadean/scenes/MenuScene.java b/src/main/java/xyz/valnet/hadean/scenes/MenuScene.java new file mode 100644 index 0000000..fcaa5a5 --- /dev/null +++ b/src/main/java/xyz/valnet/hadean/scenes/MenuScene.java @@ -0,0 +1,78 @@ +package xyz.valnet.hadean.scenes; + +import xyz.valnet.engine.math.Vector4f; +import xyz.valnet.engine.scenegraph.IScene; +import xyz.valnet.hadean.input.Button; +import xyz.valnet.hadean.input.IButtonListener; +import xyz.valnet.hadean.util.Assets; + +import static xyz.valnet.hadean.HadeanGame.Hadean; + +public class MenuScene implements IScene, IButtonListener { + + private Button btnNewGame = new Button(Assets.frame, "New Game", 50, 200, 128, 32); + private Button btnLoadGame = new Button(Assets.frame, "Load Game", 50, 240, 128, 32); + private Button btnOptions = new Button(Assets.frame, "Options", 50, 280, 128, 32); + private Button btnQuit = new Button(Assets.frame, "Quit", 50, 320, 128, 32); + + public MenuScene() { + btnNewGame.registerClickListener(this); + btnLoadGame.registerClickListener(this); + btnOptions.registerClickListener(this); + btnQuit.registerClickListener(this); + } + + public Vector4f green = new Vector4f(0.0f, 1.0f, 0.2f, 1.0f); + public Vector4f cyan = new Vector4f(0.1f, 0.7f, 1.0f, 1.0f); + public Vector4f yellow = new Vector4f(1.0f, 1.0f, 0.0f, 1.0f); + public Vector4f red = new Vector4f(1.0f, 0.1f, 0.1f, 1.0f); + + @Override + public void render() { + Assets.flat.pushColor(green); + btnNewGame.draw(); + Assets.flat.swapColor(cyan); + btnLoadGame.draw(); + Assets.flat.swapColor(yellow); + btnOptions.draw(); + Assets.flat.swapColor(red); + btnQuit.draw(); + Assets.flat.popColor(); + } + + @Override + public void update(float dTime) { + btnNewGame.update(); + btnLoadGame.update(); + btnOptions.update(); + btnQuit.update(); + } + + @Override + public void click(Button target) { + if(target == btnNewGame) { + newGame(); + } else if(target == btnQuit) { + quit(); + } + } + + private void newGame() { + Hadean.changeScene(new GameScene()); + } + + private void quit() { + + } + + @Override + public void enable() { + + } + + @Override + public void disable() { + + } + +} diff --git a/src/main/java/xyz/valnet/hadean/util/Assets.java b/src/main/java/xyz/valnet/hadean/util/Assets.java new file mode 100644 index 0000000..cd567d3 --- /dev/null +++ b/src/main/java/xyz/valnet/hadean/util/Assets.java @@ -0,0 +1,173 @@ +package xyz.valnet.hadean.util; + +import java.util.HashMap; +import java.util.Map; + +import xyz.valnet.engine.graphics.Font; +import xyz.valnet.engine.graphics.Sprite; +import xyz.valnet.engine.graphics.Texture; +import xyz.valnet.engine.graphics.Tile9; +import xyz.valnet.engine.math.Vector4i; +import xyz.valnet.engine.shaders.SimpleShader; + +public class Assets { + + public static final Texture atlas; + public static final Font font; + public static final Tile9 redFrame; + public static final Tile9 frame; + public static final Tile9 fireFrame; + + public static final Sprite[] defaultTerrain; + + public static final SimpleShader flat; + + static { + flat = new SimpleShader("shaders/flat.vert", "shaders/flat.frag"); + + atlas = new Texture("res/textures.png"); + + defaultTerrain = new Sprite[] { + new Sprite(atlas, new Vector4i(24, 72, 8, 8)), + new Sprite(atlas, new Vector4i(24, 64, 8, 8)), + new Sprite(atlas, new Vector4i(32, 72, 8, 8)), + new Sprite(atlas, new Vector4i(32, 64, 8, 8)) + }; + + Map charset = new HashMap(); + + charset.put('A', new Sprite(atlas, new Vector4i( 0, 0, 8, 16))); + charset.put('B', new Sprite(atlas, new Vector4i( 8, 0, 8, 16))); + charset.put('C', new Sprite(atlas, new Vector4i( 16, 0, 8, 16))); + charset.put('D', new Sprite(atlas, new Vector4i( 24, 0, 8, 16))); + charset.put('E', new Sprite(atlas, new Vector4i( 32, 0, 8, 16))); + charset.put('F', new Sprite(atlas, new Vector4i( 40, 0, 8, 16))); + charset.put('G', new Sprite(atlas, new Vector4i( 48, 0, 8, 16))); + charset.put('H', new Sprite(atlas, new Vector4i( 56, 0, 8, 16))); + charset.put('I', new Sprite(atlas, new Vector4i( 64, 0, 8, 16))); + charset.put('J', new Sprite(atlas, new Vector4i( 72, 0, 8, 16))); + charset.put('K', new Sprite(atlas, new Vector4i( 80, 0, 8, 16))); + charset.put('L', new Sprite(atlas, new Vector4i( 88, 0, 8, 16))); + charset.put('M', new Sprite(atlas, new Vector4i( 96, 0, 8, 16))); + charset.put('N', new Sprite(atlas, new Vector4i(104, 0, 8, 16))); + charset.put('O', new Sprite(atlas, new Vector4i(112, 0, 8, 16))); + charset.put('P', new Sprite(atlas, new Vector4i(120, 0, 8, 16))); + charset.put('Q', new Sprite(atlas, new Vector4i(128, 0, 8, 16))); + charset.put('R', new Sprite(atlas, new Vector4i(136, 0, 8, 16))); + charset.put('S', new Sprite(atlas, new Vector4i(144, 0, 8, 16))); + charset.put('T', new Sprite(atlas, new Vector4i(152, 0, 8, 16))); + charset.put('U', new Sprite(atlas, new Vector4i(160, 0, 8, 16))); + charset.put('V', new Sprite(atlas, new Vector4i(168, 0, 8, 16))); + charset.put('W', new Sprite(atlas, new Vector4i(176, 0, 8, 16))); + charset.put('X', new Sprite(atlas, new Vector4i(184, 0, 8, 16))); + charset.put('Y', new Sprite(atlas, new Vector4i(192, 0, 8, 16))); + charset.put('Z', new Sprite(atlas, new Vector4i(200, 0, 8, 16))); + charset.put(':', new Sprite(atlas, new Vector4i(208, 0, 8, 16))); + charset.put(';', new Sprite(atlas, new Vector4i(216, 0, 8, 16))); + charset.put('.', new Sprite(atlas, new Vector4i(224, 0, 8, 16))); + charset.put(',', new Sprite(atlas, new Vector4i(232, 0, 8, 16))); + charset.put('!', new Sprite(atlas, new Vector4i(240, 0, 8, 16))); + charset.put('?', new Sprite(atlas, new Vector4i(248, 0, 8, 16))); + + charset.put('a', new Sprite(atlas, new Vector4i( 0, 16, 8, 16))); + charset.put('b', new Sprite(atlas, new Vector4i( 8, 16, 8, 16))); + charset.put('c', new Sprite(atlas, new Vector4i( 16, 16, 8, 16))); + charset.put('d', new Sprite(atlas, new Vector4i( 24, 16, 8, 16))); + charset.put('e', new Sprite(atlas, new Vector4i( 32, 16, 8, 16))); + charset.put('f', new Sprite(atlas, new Vector4i( 40, 16, 8, 16))); + charset.put('g', new Sprite(atlas, new Vector4i( 48, 16, 8, 16))); + charset.put('h', new Sprite(atlas, new Vector4i( 56, 16, 8, 16))); + charset.put('i', new Sprite(atlas, new Vector4i( 64, 16, 8, 16))); + charset.put('j', new Sprite(atlas, new Vector4i( 72, 16, 8, 16))); + charset.put('k', new Sprite(atlas, new Vector4i( 80, 16, 8, 16))); + charset.put('l', new Sprite(atlas, new Vector4i( 88, 16, 8, 16))); + charset.put('m', new Sprite(atlas, new Vector4i( 96, 16, 8, 16))); + charset.put('n', new Sprite(atlas, new Vector4i(104, 16, 8, 16))); + charset.put('o', new Sprite(atlas, new Vector4i(112, 16, 8, 16))); + charset.put('p', new Sprite(atlas, new Vector4i(120, 16, 8, 16))); + charset.put('q', new Sprite(atlas, new Vector4i(128, 16, 8, 16))); + charset.put('r', new Sprite(atlas, new Vector4i(136, 16, 8, 16))); + charset.put('s', new Sprite(atlas, new Vector4i(144, 16, 8, 16))); + charset.put('t', new Sprite(atlas, new Vector4i(152, 16, 8, 16))); + charset.put('u', new Sprite(atlas, new Vector4i(160, 16, 8, 16))); + charset.put('v', new Sprite(atlas, new Vector4i(168, 16, 8, 16))); + charset.put('w', new Sprite(atlas, new Vector4i(176, 16, 8, 16))); + charset.put('x', new Sprite(atlas, new Vector4i(184, 16, 8, 16))); + charset.put('y', new Sprite(atlas, new Vector4i(192, 16, 8, 16))); + charset.put('z', new Sprite(atlas, new Vector4i(200, 16, 8, 16))); + charset.put('\'', new Sprite(atlas, new Vector4i(208, 16, 8, 16))); + charset.put('"', new Sprite(atlas, new Vector4i(216, 16, 8, 16))); + charset.put('[', new Sprite(atlas, new Vector4i(224, 16, 8, 16))); + charset.put(']', new Sprite(atlas, new Vector4i(232, 16, 8, 16))); + charset.put('{', new Sprite(atlas, new Vector4i(240, 16, 8, 16))); + charset.put('}', new Sprite(atlas, new Vector4i(248, 16, 8, 16))); + + charset.put('0', new Sprite(atlas, new Vector4i( 0, 32, 8, 16))); + charset.put('1', new Sprite(atlas, new Vector4i( 8, 32, 8, 16))); + charset.put('2', new Sprite(atlas, new Vector4i( 16, 32, 8, 16))); + charset.put('3', new Sprite(atlas, new Vector4i( 24, 32, 8, 16))); + charset.put('4', new Sprite(atlas, new Vector4i( 32, 32, 8, 16))); + charset.put('5', new Sprite(atlas, new Vector4i( 40, 32, 8, 16))); + charset.put('6', new Sprite(atlas, new Vector4i( 48, 32, 8, 16))); + charset.put('7', new Sprite(atlas, new Vector4i( 56, 32, 8, 16))); + charset.put('8', new Sprite(atlas, new Vector4i( 64, 32, 8, 16))); + charset.put('9', new Sprite(atlas, new Vector4i( 72, 32, 8, 16))); + charset.put('@', new Sprite(atlas, new Vector4i( 80, 32, 8, 16))); + charset.put('#', new Sprite(atlas, new Vector4i( 88, 32, 8, 16))); + charset.put('$', new Sprite(atlas, new Vector4i( 96, 32, 8, 16))); + charset.put('%', new Sprite(atlas, new Vector4i(104, 32, 8, 16))); + charset.put('^', new Sprite(atlas, new Vector4i(112, 32, 8, 16))); + charset.put('&', new Sprite(atlas, new Vector4i(120, 32, 8, 16))); + charset.put('*', new Sprite(atlas, new Vector4i(128, 32, 8, 16))); + charset.put('(', new Sprite(atlas, new Vector4i(136, 32, 8, 16))); + charset.put(')', new Sprite(atlas, new Vector4i(144, 32, 8, 16))); + charset.put('<', new Sprite(atlas, new Vector4i(152, 32, 8, 16))); + charset.put('>', new Sprite(atlas, new Vector4i(160, 32, 8, 16))); + charset.put('_', new Sprite(atlas, new Vector4i(168, 32, 8, 16))); + charset.put('-', new Sprite(atlas, new Vector4i(176, 32, 8, 16))); + charset.put('+', new Sprite(atlas, new Vector4i(184, 32, 8, 16))); + charset.put('=', new Sprite(atlas, new Vector4i(192, 32, 8, 16))); + charset.put('/', new Sprite(atlas, new Vector4i(200, 32, 8, 16))); + charset.put('\\', new Sprite(atlas, new Vector4i(208, 32, 8, 16))); + charset.put('♥', new Sprite(atlas, new Vector4i(216, 32, 8, 16))); + charset.put('|', new Sprite(atlas, new Vector4i(224, 32, 8, 16))); + font = new Font(charset, 8, 16); + + frame = new Tile9( + new Sprite(atlas, new Vector4i(24, 88, 8, 8)), + new Sprite(atlas, new Vector4i(32, 88, 8, 8)), + new Sprite(atlas, new Vector4i(40, 88, 8, 8)), + new Sprite(atlas, new Vector4i(24, 96, 8, 8)), + new Sprite(atlas, new Vector4i(32, 96, 8, 8)), + new Sprite(atlas, new Vector4i(40, 96, 8, 8)), + new Sprite(atlas, new Vector4i(24, 104, 8, 8)), + new Sprite(atlas, new Vector4i(32, 104, 8, 8)), + new Sprite(atlas, new Vector4i(40, 104, 8, 8)) + ); + + redFrame = new Tile9( + new Sprite(atlas, new Vector4i( 0, 88, 8, 8)), + new Sprite(atlas, new Vector4i( 8, 88, 8, 8)), + new Sprite(atlas, new Vector4i(16, 88, 8, 8)), + new Sprite(atlas, new Vector4i( 0, 96, 8, 8)), + new Sprite(atlas, new Vector4i( 8, 96, 8, 8)), + new Sprite(atlas, new Vector4i(16, 96, 8, 8)), + new Sprite(atlas, new Vector4i( 0, 104, 8, 8)), + new Sprite(atlas, new Vector4i( 8, 104, 8, 8)), + new Sprite(atlas, new Vector4i(16, 104, 8, 8)) + ); + + fireFrame = new Tile9( + new Sprite(atlas, new Vector4i( 0, 88 - 24, 8, 8)), + new Sprite(atlas, new Vector4i( 8, 88 - 24, 8, 8)), + new Sprite(atlas, new Vector4i(16, 88 - 24, 8, 8)), + new Sprite(atlas, new Vector4i( 0, 96 - 24, 8, 8)), + new Sprite(atlas, new Vector4i( 8, 96 - 24, 8, 8)), + new Sprite(atlas, new Vector4i(16, 96 - 24, 8, 8)), + new Sprite(atlas, new Vector4i( 0, 104 - 24, 8, 8)), + new Sprite(atlas, new Vector4i( 8, 104 - 24, 8, 8)), + new Sprite(atlas, new Vector4i(16, 104 - 24, 8, 8)) + ); + + } +}