pull/1/head
Valerie 2022-05-18 07:46:03 -04:00
commit 5dd6d8579b
35 changed files with 1613 additions and 0 deletions

1
.gitignore vendored 100644
View File

@ -0,0 +1 @@
target

20
.vscode/launch.json vendored 100644
View File

@ -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"
}
}
]
}

3
.vscode/settings.json vendored 100644
View File

@ -0,0 +1,3 @@
{
"java.configuration.updateBuildConfiguration": "automatic"
}

139
pom.xml 100644
View File

@ -0,0 +1,139 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>xyz.valnet.hadean</groupId>
<artifactId>hadean</artifactId>
<version>1.0-SNAPSHOT</version>
<name>hadean</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<lwjgl.version>3.3.1</lwjgl.version>
<lwjgl.natives>natives-windows</lwjgl.natives>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-bom</artifactId>
<version>${lwjgl.version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl</artifactId>
</dependency>
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-assimp</artifactId>
</dependency>
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-glfw</artifactId>
</dependency>
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-openal</artifactId>
</dependency>
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-opengl</artifactId>
</dependency>
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-stb</artifactId>
</dependency>
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl</artifactId>
<classifier>${lwjgl.natives}</classifier>
</dependency>
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-assimp</artifactId>
<classifier>${lwjgl.natives}</classifier>
</dependency>
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-glfw</artifactId>
<classifier>${lwjgl.natives}</classifier>
</dependency>
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-openal</artifactId>
<classifier>${lwjgl.natives}</classifier>
</dependency>
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-opengl</artifactId>
<classifier>${lwjgl.natives}</classifier>
</dependency>
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-stb</artifactId>
<classifier>${lwjgl.natives}</classifier>
</dependency>
</dependencies>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

BIN
res/textures.pdn 100644

Binary file not shown.

BIN
res/textures.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

15
shaders/flat.frag 100644
View File

@ -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;
}
}

17
shaders/flat.vert 100644
View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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();
}
}

View File

@ -0,0 +1,55 @@
package xyz.valnet.engine.graphics;
import java.util.Map;
import xyz.valnet.engine.math.Vector4i;
public class Font {
private Map<Character, Sprite> charset;
private final int w, h;
public Font(Map<Character, Sprite> 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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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 + ")";
}
}

View File

@ -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;
}
}

View File

@ -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) {}
}

View File

@ -0,0 +1,5 @@
package xyz.valnet.engine.scenegraph;
public interface IRenderable {
public void render();
}

View File

@ -0,0 +1,6 @@
package xyz.valnet.engine.scenegraph;
public interface IRenderableListener {
public void addRenderable(IRenderable renderable);
public void removeRenderable(IRenderable renderable);
}

View File

@ -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();
}

View File

@ -0,0 +1,5 @@
package xyz.valnet.engine.scenegraph;
public interface ITickable {
public void tick(float dTime);
}

View File

@ -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<String, Integer> locationCache = new HashMap<String, Integer>();
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;
}
}

View File

@ -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<Vector4f> colorStack = new Stack<Vector4f>();
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");
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,5 @@
package xyz.valnet.hadean.input;
public interface IButtonListener {
public void click(Button target);
}

View File

@ -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<GameObject> objects = new ArrayList<GameObject>();
// private List<IRenderable> renderables = new ArrayList<IRenderable>();
// 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();
}
}

View File

@ -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() {
}
}

View File

@ -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<Character, Sprite> charset = new HashMap<Character, Sprite>();
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))
);
}
}