Text boxes render!

This commit is contained in:
austin 2021-06-01 22:41:16 -04:00
parent 31e506ff1f
commit f4a7a8d513
21 changed files with 1039 additions and 323 deletions

View File

@ -35,6 +35,13 @@ public class ControlHandler {
public static final String DATA_STRING_INPUT_CODE_MOVEMENT_FALL = "fall";
enum ControlsState {
TITLE_PAGE,
TITLE_MENU,
MAIN_GAME,
}
HashMap<String, Integer> controlsMap;
HashMap<String, Boolean> controlsState;

View File

@ -36,6 +36,7 @@ public class DrawCellManager {
int drawStepdownInterval = 3;
int drawStepdownValue = 5;
public DrawCellManager(TerrainManager terrainManager, float realX, float realY){
this.terrainManager = terrainManager;
this.miniCellWidth = miniCellWidth;

View File

@ -0,0 +1,241 @@
package electrosphere.game.state;
import electrosphere.entity.CameraEntityUtils;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityUtils;
import electrosphere.game.cell.DrawCellManager;
import electrosphere.game.state.SimulationState.SimulationStateMachine;
import electrosphere.game.terrain.TerrainManager;
import electrosphere.main.Globals;
import electrosphere.net.client.ClientNetworking;
import electrosphere.net.server.Server;
import electrosphere.renderer.Model;
import electrosphere.renderer.RenderUtils;
import java.util.concurrent.Semaphore;
import org.joml.Vector3f;
/**
*
* @author amaterasu
*/
public class LoadingThread extends Thread {
public static final int LOAD_TITLE_MENU = 0;
public static final int LOAD_MAIN_GAME = 1;
public static final int LOAD_ARENA = 2;
int threadType;
Semaphore lock;
public LoadingThread(int type){
threadType = type;
lock = new Semaphore(1);
}
@Override
public void run(){
lock.acquireUninterruptibly();
switch(threadType){
case LOAD_TITLE_MENU:
SimulationState.simulationState = SimulationStateMachine.TITLE_MENU;
break;
case LOAD_MAIN_GAME:
//initialize the terrain manager (server only)
initTerrainManager();
//initialize the server thread (server only)
initServerThread();
//initialize the client thread (client)
initClientThread();
//initialize the cell manager (client)
initCellManager();
//initialize the basic graphical entities of the world (skybox, camera)
initWorldBaseGraphicalEntities();
Globals.RENDER_FLAG_RENDER_SHADOW_MAP = true;
Globals.RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER_CONTENT = true;
Globals.RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER = true;
Globals.RENDER_FLAG_RENDER_UI = true;
SimulationState.simulationState = SimulationStateMachine.MAIN_SIMULATION;
break;
case LOAD_ARENA:
SimulationState.simulationState = SimulationStateMachine.ARENA_SIMULATION;
break;
}
lock.release();
}
public boolean isDone(){
boolean rVal = lock.tryAcquire();
if(rVal == true){
lock.release();
}
return rVal;
}
static void initTerrainManager(){
/*
Actually initialize the terrain manager
*/
float[][] elevation;
Globals.terrainManager = new TerrainManager(2000,200,100,0.25f);
if(Globals.mainConfig.runServer){
if(Globals.mainConfig.loadTerrain){
Globals.terrainManager.load();
} else {
Globals.terrainManager.generate();
Globals.terrainManager.save();
}
}
/*
Set spawn point
*/
int playerStartX = 0;
int playerStartY = 0;
int discreteSize = Globals.terrainManager.getWorldDiscreteSize();
int chunkSize = Globals.terrainManager.getChunkWidth();
boolean found = false;
for(int x = 0; x < discreteSize; x++){
for(int y = 0; y < discreteSize; y++){
if(Globals.terrainManager.getDiscreteValue(x, y)>0){
playerStartX = x;
playerStartY = y;
found = true;
}
if(found){
break;
}
}
if(found){
break;
}
}
Globals.spawnPoint = new Vector3f(playerStartX * chunkSize,0,playerStartY * chunkSize);
}
static void initServerThread(){
//start server networking
Thread serverThread = null;
if(Globals.mainConfig.runServer){
Globals.server = new Server(42536);
serverThread = new Thread(Globals.server);
serverThread.start();
}
}
static void initClientThread(){
//start client networking
Thread clientThread = null;
if(Globals.mainConfig.runClient){
Globals.clientConnection = new ClientNetworking(Globals.mainConfig.serverAddress,42536);
clientThread = new Thread(Globals.clientConnection);
clientThread.start();
}
}
static void initCellManager(){
Globals.drawCellManager = new DrawCellManager(Globals.terrainManager, Globals.spawnPoint.x, Globals.spawnPoint.z);
while(Globals.drawCellManager.containsInvalidCell()){
Globals.drawCellManager.updateInvalidCell();
}
while(Globals.drawCellManager.containsUndrawableCell()){
Globals.drawCellManager.makeCellDrawable();
}
}
static void initWorldBaseGraphicalEntities(){
/*
Skybox
*/
Model skyboxModel = RenderUtils.createSkyboxModel(null);
String skyboxModelPath = Globals.assetManager.registerModel(skyboxModel);
Entity skyboxEntity = EntityUtils.spawnDrawableEntity(skyboxModelPath);
EntityUtils.getEntityScale(skyboxEntity).mul(100);
Globals.skyboxColors.add(new Vector3f(100,150,200));
Globals.skyboxColors.add(new Vector3f(100,150,200));
Globals.skyboxColors.add(new Vector3f(50,100,150));
Globals.skyboxColors.add(new Vector3f(50,100,150));
Globals.skyboxColors.add(new Vector3f(100,150,200));
Globals.skyboxColors.add(new Vector3f(100,150,200));
Globals.skyboxColors.add(new Vector3f(50,100,150));
Globals.skyboxColors.add(new Vector3f(50,100,150));
/*
Player Camera
*/
Globals.playerCamera = CameraEntityUtils.spawnBasicCameraEntity(new Vector3f(1,0,1), new Vector3f(0,0,0));
}
}

View File

@ -0,0 +1,17 @@
package electrosphere.game.state;
/**
*
* @author amaterasu
*/
public class SimulationState {
public enum SimulationStateMachine {
LOADING,
TITLE_MENU,
MAIN_SIMULATION,
ARENA_SIMULATION,
}
public static SimulationStateMachine simulationState;
}

View File

@ -16,10 +16,16 @@ import electrosphere.entity.EntityManager;
import electrosphere.entity.types.creature.CreatureType;
import electrosphere.entity.types.creature.CreatureTypeList;
import electrosphere.game.cell.DrawCellManager;
import electrosphere.game.state.LoadingThread;
import electrosphere.game.terrain.TerrainManager;
import electrosphere.net.client.ClientNetworking;
import electrosphere.net.server.Server;
import electrosphere.renderer.ModelUtils;
import electrosphere.renderer.assetmanager.AssetDataStrings;
import electrosphere.renderer.assetmanager.AssetManager;
import electrosphere.renderer.ui.WidgetManager;
import electrosphere.renderer.ui.font.FontUtils;
import electrosphere.renderer.ui.font.RawFontMap;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
@ -83,16 +89,27 @@ public class Globals {
//
//OpenGL - Other
//
//Array that holds all models that need to be shoved to the gpu
public static int WINDOW_WIDTH = 1920;
public static int WINDOW_HEIGHT = 1080;
//matrices for drawing models
public static Matrix4f viewMatrix = new Matrix4f();
public static Matrix4f projectionMatrix;
public static Matrix4f lightDepthMatrix = new Matrix4f();
//locations for shadow map specific variables
public static int shadowMapTextureLoc = 0;
public static int depthMapShaderProgramLoc = 0;
//
//OpenGL - Abstracted engine objects
//
public static Texture textureDiffuseDefault;
public static Texture textureSpecularDefault;
public static Material materialDefault;
@ -103,17 +120,24 @@ public class Globals {
public static TextureMap textureMapDefault;
//
//Engine - Main managers/variables
//
//keeps track of all entities in game
public static EntityManager entityManager;
//terrain manager
public static TerrainManager terrainManager;
public static Vector3f spawnPoint = new Vector3f(0,0,0);
//manages all models loaded into memory
public static AssetManager assetManager;
//
//Game specific models
//
//chunk stuff
//constant for how far in game units you have to move to load chunks
@ -122,8 +146,14 @@ public class Globals {
//famous fuckin last words, but temporary solution
//global arraylist of values for the skybox colors
//edit(6/1/21): :upside_down_smile:
public static ArrayList<Vector3f> skyboxColors;
//thread for loading different game states
public static LoadingThread loadingThread;
//manager for all widgets currently being drawn to screen
public static WidgetManager widgetManager;
//the player camera entity
@ -134,28 +164,37 @@ public class Globals {
public static boolean RENDER_FLAG_RENDER_SHADOW_MAP = false;
public static boolean RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER_CONTENT = false;
public static boolean RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER = false;
public static boolean RENDER_FLAG_RENDER_UI = true;
public static void initGlobals(){
//load in default texture map
Gson gson = new Gson();
try {
// try {
//deserializes the texture map from its default path using gson
//also done in one line
textureMapDefault = gson.fromJson(Files.newBufferedReader(new File(Thread.currentThread().getContextClassLoader().getResource("Textures/default_texture_map.json").getFile()).toPath()), TextureMap.class); //only the best of coding practices :)
} catch (IOException ex) { ex.printStackTrace(); } //TODO: handle better :tm:
textureMapDefault = Utilities.loadObjectFromBakedJsonFile("/Textures/default_texture_map.json", TextureMap.class);
// textureMapDefault = gson.fromJson(Files.newBufferedReader(new File(Thread.currentThread().getContextClassLoader().getResource("Textures/default_texture_map.json").getFile()).toPath()), TextureMap.class); //only the best of coding practices :)
// } catch (IOException ex) { ex.printStackTrace(); } //TODO: handle better :tm:
//entity type map
initEntityTypeMap();
//create entity manager
entityManager = new EntityManager();
//init game specific variables
initGameSpecifics();
//temporary hold for skybox colors
skyboxColors = new ArrayList();
//load asset manager
assetManager = new AssetManager();
//load widget manager
widgetManager = new WidgetManager();
}
public static void initDefaultResources(){
public static void initDefaultGraphicalResources(){
//create default textures
textureDiffuseDefault = new Texture("Textures/default_diffuse.png");
textureSpecularDefault = new Texture("Textures/default_specular.png");
@ -165,9 +204,9 @@ public class Globals {
materialDefault.set_specular(textureSpecularDefault);
//create default lights
lightDirectionalDefault = new DirectionalLight(new Vector3f(0,-1f,0));
}
public static void initGameSpecifics(){
assetManager.registerModelToSpecificString(ModelUtils.createBitmapDisplay(), AssetDataStrings.ASSET_STRING_BITMAP_FONT);
RawFontMap fontMap = Utilities.loadObjectFromBakedJsonFile("/Textures/Fonts/fontMap.json", RawFontMap.class);
FontUtils.setFontDataMap(fontMap);
}
static void initEntityTypeMap(){

View File

@ -15,6 +15,9 @@ import electrosphere.entity.Entity;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.state.MovementTree;
import electrosphere.game.cell.DrawCellManager;
import electrosphere.game.state.LoadingThread;
import electrosphere.game.state.SimulationState;
import electrosphere.game.state.SimulationState.SimulationStateMachine;
import electrosphere.game.terrain.TerrainManager;
import electrosphere.net.client.ClientNetworkMessage;
import electrosphere.net.client.ClientNetworking;
@ -22,6 +25,8 @@ import electrosphere.net.server.Server;
import electrosphere.renderer.Actor;
import electrosphere.renderer.ui.font.FontUtils;
import electrosphere.renderer.ModelUtils;
import electrosphere.renderer.ui.WidgetUtils;
import electrosphere.renderer.ui.font.TextBox;
import electrosphere.terraingen.TerrainGen;
import electrosphere.terraingen.models.TerrainModel;
import java.util.ArrayList;
@ -109,83 +114,38 @@ public class Main {
public static void main(String args[]){
Utilities.loadMainConfig();
//
//
// I N I T I A L I Z A T I O N
//
//
//set simulation status to loading title menu
SimulationState.simulationState = SimulationStateMachine.LOADING;
//init basic config
Utilities.loadMainConfig();
//controls
initControlHandler();
//init global variables
Globals.initGlobals();
//run initialization stuff
initTerrainManager();
if(Globals.mainConfig.runRenderer){
//Create opengl
RenderUtils.createOpenglContext();
}
//start server networking
Thread serverThread = null;
if(Globals.mainConfig.runServer){
Globals.server = new Server(42536);
serverThread = new Thread(Globals.server);
serverThread.start();
}
//start client networking
Thread clientThread = null;
if(Globals.mainConfig.runClient){
Globals.clientConnection = new ClientNetworking(Globals.mainConfig.serverAddress,42536);
clientThread = new Thread(Globals.clientConnection);
clientThread.start();
}
//create the drawing context
RenderUtils.createOpenglContext();
//init default resources
Globals.initDefaultResources();
Globals.initDefaultGraphicalResources();
if(Globals.mainConfig.runRenderer){
initCellManager();
}
//fire off a loading thread for the title menus/screen
Globals.loadingThread = new LoadingThread(LoadingThread.LOAD_TITLE_MENU);
Globals.loadingThread.start();
initSkybox();
TextBox loadingText = WidgetUtils.createVerticallyAlignedTextBox(520, 100, 50, 7, 1, "LOADING", true);
initPlayer();
createPlayerCamera();
// String unitCubeModelPath = Globals.assetManager.registerModel(ModelUtils.createUnitCube());
// Entity unitCube = EntityUtils.spawnDrawableEntity(unitCubeModelPath);
// EntityUtils.getEntityPosition(unitCube).set(10,2,10);
// String goundPlaneModelPath = "Models/groundplanemassiveuv.fbx";
// Entity groundPlane = EntityUtils.spawnDrawableEntity(goundPlaneModelPath);
// EntityUtils.getEntityPosition(groundPlane).set(10f,2f,10f);
// EntityUtils.getEntityRotation(groundPlane).rotateAxis((float)Math.PI/2, new Vector3f(1,0,0));
// EntityUtils.getEntityScale(groundPlane).set(5);
String unitsphereModelPath = "Models/unitsphere.fbx";
Entity unitsphere = EntityUtils.spawnDrawableEntity(unitsphereModelPath);
EntityUtils.getEntityPosition(unitsphere).set(10f,2f,10f);
EntityUtils.getEntityScale(unitsphere).set(1);
Entity font = FontUtils.makeFont(7, 1);
EntityUtils.getEntityPosition(font).set(new Vector3f(0.2f,0.2f,0.0f));
for(int i = 0; i < 10; i++){
Random rand = new Random();
Entity creature = CreatureUtils.spawnBasicCreature(0, 0.01f, 0.01f);
EntityUtils.getEntityPosition(creature).set(rand.nextFloat() * 10, rand.nextFloat() * 10, rand.nextFloat() * 10);
EntityUtils.getEntityScale(creature).set(0.01f);
}
//recapture the screen for rendering
RenderUtils.recaptureScreen();
///
@ -219,7 +179,7 @@ public class Main {
/// C L I E N T N E T W O R K I N G S T U F F
///
//Why is this its own function? Just to get the networking code out of main()
if(Globals.mainConfig.runClient){
if(Globals.mainConfig.runClient && Globals.clientConnection != null){
Globals.clientConnection.parseMessages();
}
@ -233,12 +193,15 @@ public class Main {
break;
}
//Poll controls
//cell tracking values
Vector3f oldPlayerCharacterPosition = new Vector3f();
if(Globals.playerCharacter != null){
oldPlayerCharacterPosition = new Vector3f(EntityUtils.getEntityPosition(Globals.playerCharacter));
}
Vector3f newPlayerCharacterPosition = oldPlayerCharacterPosition;
//Poll controls
Globals.controlHandler.pollControls();
@ -246,7 +209,7 @@ public class Main {
///
/// C L I E N T S I M U L A T I O N S T U F F
///
if(Globals.mainConfig.runClient){
if(SimulationState.simulationState == SimulationStateMachine.MAIN_SIMULATION || SimulationState.simulationState == SimulationStateMachine.ARENA_SIMULATION){
//simulate creature behavior trees
for(Entity currentMoveable : Globals.entityManager.getMoveable()){
MovementTree behaviorTree = CreatureUtils.getEntityMovementTree(currentMoveable);
@ -259,7 +222,7 @@ public class Main {
///
/// C L I E N T C E L L M A N A G E R
///
if(Globals.mainConfig.runRenderer){
if(SimulationState.simulationState == SimulationStateMachine.MAIN_SIMULATION){
if(Globals.playerCharacter != null){
newPlayerCharacterPosition = EntityUtils.getEntityPosition(Globals.playerCharacter);
}
@ -274,19 +237,24 @@ public class Main {
/// C A M E R A S T U F F
///
//poll mouse variables and update camera variables
updateMouseVariables();
if(Globals.playerCharacter != null){
CameraEntityUtils.setCameraCenter(Globals.playerCamera, EntityUtils.getEntityPosition(Globals.playerCharacter));
if(SimulationState.simulationState == SimulationStateMachine.MAIN_SIMULATION || SimulationState.simulationState == SimulationStateMachine.ARENA_SIMULATION){
updateMouseVariables();
if(Globals.playerCharacter != null){
CameraEntityUtils.setCameraCenter(Globals.playerCamera, EntityUtils.getEntityPosition(Globals.playerCharacter));
}
float cam_Player_Orbit_Magnitude = 1f;
cameraRotationVector.x = 0 + (float) Math.cos(yaw / 180.0f * Math.PI) * cam_Player_Orbit_Magnitude;
cameraRotationVector.y = 0 + (float) Math.sin(pitch / 180.0f * Math.PI) * cam_Player_Orbit_Magnitude;
cameraRotationVector.z = 0 + (float) Math.sin(yaw / 180.0f * Math.PI) * cam_Player_Orbit_Magnitude;
cameraRotationVector.normalize();
CameraEntityUtils.setCameraEye(Globals.playerCamera, cameraRotationVector);
Globals.viewMatrix = CameraEntityUtils.getCameraViewMatrix(CameraEntityUtils.getCameraEye(Globals.playerCamera));
}
float cam_Player_Orbit_Magnitude = 1f;
// cam_Player_Orbit.pos_Center = new Vector3f(0, 0, 0);
cameraRotationVector.x = 0 + (float) Math.cos(yaw / 180.0f * Math.PI) * cam_Player_Orbit_Magnitude;
cameraRotationVector.y = 0 + (float) Math.sin(pitch / 180.0f * Math.PI) * cam_Player_Orbit_Magnitude;
cameraRotationVector.z = 0 + (float) Math.sin(yaw / 180.0f * Math.PI) * cam_Player_Orbit_Magnitude;
cameraRotationVector.normalize();
CameraEntityUtils.setCameraEye(Globals.playerCamera, cameraRotationVector);
// System.out.println(cam_Player_Orbit.pos_Center);
Globals.viewMatrix = CameraEntityUtils.getCameraViewMatrix(CameraEntityUtils.getCameraEye(Globals.playerCamera));
@ -301,7 +269,9 @@ public class Main {
glfwTerminate();
//used to signal threads to stop
running = false;
Globals.server.close();
if(Globals.server != null){
Globals.server.close();
}
}
static void sleep(int i) {
@ -348,101 +318,31 @@ public class Main {
}
}
static void initSkybox(){
Model skyboxModel = RenderUtils.createSkyboxModel(null);
String skyboxModelPath = Globals.assetManager.registerModel(skyboxModel);
Entity skyboxEntity = EntityUtils.spawnDrawableEntity(skyboxModelPath);
EntityUtils.getEntityScale(skyboxEntity).mul(100);
Globals.skyboxColors.add(new Vector3f(100,150,200));
Globals.skyboxColors.add(new Vector3f(100,150,200));
Globals.skyboxColors.add(new Vector3f(50,100,150));
Globals.skyboxColors.add(new Vector3f(50,100,150));
Globals.skyboxColors.add(new Vector3f(100,150,200));
Globals.skyboxColors.add(new Vector3f(100,150,200));
Globals.skyboxColors.add(new Vector3f(50,100,150));
Globals.skyboxColors.add(new Vector3f(50,100,150));
}
static void creatingRandomEntities(){
// String unitCubeModelPath = Globals.assetManager.registerModel(ModelUtils.createUnitCube());
// Entity unitCube = EntityUtils.spawnDrawableEntity(unitCubeModelPath);
// EntityUtils.getEntityPosition(unitCube).set(10,2,10);
// String goundPlaneModelPath = "Models/groundplanemassiveuv.fbx";
// Entity groundPlane = EntityUtils.spawnDrawableEntity(goundPlaneModelPath);
// EntityUtils.getEntityPosition(groundPlane).set(10f,2f,10f);
// EntityUtils.getEntityRotation(groundPlane).rotateAxis((float)Math.PI/2, new Vector3f(1,0,0));
// EntityUtils.getEntityScale(groundPlane).set(5);
static void initTerrainManager(){
String unitsphereModelPath = "Models/unitsphere.fbx";
Entity unitsphere = EntityUtils.spawnDrawableEntity(unitsphereModelPath);
EntityUtils.getEntityPosition(unitsphere).set(10f,2f,10f);
EntityUtils.getEntityScale(unitsphere).set(1);
float[][] elevation;
Globals.terrainManager = new TerrainManager(2000,200,100,0.25f);
if(Globals.mainConfig.runServer){
if(Globals.mainConfig.loadTerrain){
Globals.terrainManager.load();
} else {
Globals.terrainManager.generate();
Globals.terrainManager.save();
}
}
// Entity font = FontUtils.makeFont(7, 1);
// EntityUtils.getEntityPosition(font).set(new Vector3f(0.2f,0.2f,0.0f));
// elevation = terrainManager.getTerrainAtChunk(10, 10);
// Model terrainModel = Utilities.create_terrain_model(elevation,10);
// Entity terrainEntity = EntityUtil.spawnDrawableEntity(terrainModel);
}
static void initCellManager(){
Globals.drawCellManager = new DrawCellManager(Globals.terrainManager, playerStartRealX, playerStartRealY);
while(Globals.drawCellManager.containsInvalidCell()){
Globals.drawCellManager.updateInvalidCell();
}
while(Globals.drawCellManager.containsUndrawableCell()){
Globals.drawCellManager.makeCellDrawable();
for(int i = 0; i < 10; i++){
Random rand = new Random();
Entity creature = CreatureUtils.spawnBasicCreature(0, 0.01f, 0.01f);
EntityUtils.getEntityPosition(creature).set(rand.nextFloat() * 10, rand.nextFloat() * 10, rand.nextFloat() * 10);
EntityUtils.getEntityScale(creature).set(0.01f);
}
}
static void initPlayer(){
int playerStartX = 0;
int playerStartY = 0;
int discreteSize = Globals.terrainManager.getWorldDiscreteSize();
int chunkSize = Globals.terrainManager.getChunkWidth();
boolean found = false;
for(int x = 0; x < discreteSize; x++){
for(int y = 0; y < discreteSize; y++){
if(Globals.terrainManager.getDiscreteValue(x, y)>0){
playerStartX = x;
playerStartY = y;
found = true;
}
if(found){
break;
}
}
if(found){
break;
}
}
playerStartRealX = playerStartX * chunkSize;
playerStartRealY = playerStartY * chunkSize;
// Model homieModel = ModelLoader.load_Model_From_File("Models/person1walkanim.fbx");
// homieModel.describeAllAnimations();
// Globals.playerCharacter = CreatureUtils.spawnBasicControllableEntity(0, 0.005f, 0.025f);
// EntityUtils.getEntityScale(Globals.playerCharacter).set(0.005f);
// EntityUtils.getEntityPosition(Globals.playerCharacter).set(playerStartRealX - 0.5f,Globals.terrainManager.getHeightAtPosition(playerStartRealX, playerStartRealY),playerStartRealY - 0.5f);
// System.out.println("Player position: " + playerStartRealX + " " + playerStartRealY);
// Globals.playerCamera = CameraEntityUtils.spawnEntityTrackingCameraEntity(Globals.playerCharacter, 3f);
Globals.drawCellManager.setCellX(Globals.drawCellManager.transformRealSpaceToCellSpace(playerStartRealX));
Globals.drawCellManager.setCellY(Globals.drawCellManager.transformRealSpaceToCellSpace(playerStartRealY));
Globals.drawCellManager.invalidateAllCells();
}
static void createPlayerCamera(){
Globals.playerCamera = CameraEntityUtils.spawnBasicCameraEntity(new Vector3f(1,0,1), new Vector3f(0,0,0));
}
}

View File

@ -66,6 +66,7 @@ public class Mesh {
public int textureCoordCount;
public ArrayList<Bone> bones = new ArrayList();
public ArrayList<String> bone_id_list = new ArrayList();
HashMap<String,Object> uniforms = new HashMap();
int bone_map_size = 0;
boolean hasBones = true;
public boolean hasTextureCoords = true;
@ -638,9 +639,34 @@ public class Mesh {
}
}
//buffers contents of uniforms map to gpu
bufferAllUniforms();
glUniformMatrix4fv(glGetUniformLocation(shader.shaderProgram, "model"), false, parent.modelMatrix.get(new float[16]));
GL11.glDrawElements(GL_TRIANGLES, elementCount, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
public void setUniform(String key, Object o){
uniforms.put(key, o);
}
void bufferAllUniforms(){
for(String key : uniforms.keySet()){
Object currentUniformRaw = uniforms.get(key);
if(currentUniformRaw instanceof Matrix4f){
Matrix4f currentUniform = (Matrix4f)currentUniformRaw;
glUniformMatrix4fv(glGetUniformLocation(shader.shaderProgram, key), false, currentUniform.get(new float[16]));
}
if(currentUniformRaw instanceof Vector3f){
Vector3f currentUniform = (Vector3f)currentUniformRaw;
glUniform3fv(glGetUniformLocation(shader.shaderProgram, key), currentUniform.get(BufferUtils.createFloatBuffer(3)));
}
if(currentUniformRaw instanceof Integer){
int currentInform = (Integer)currentUniformRaw;
glUniform1i(glGetUniformLocation(shader.shaderProgram, key), currentInform);
}
}
}
}

View File

@ -353,4 +353,12 @@ public class Model {
m.drawUI();
}
}
public void pushUniformToMesh(String meshName, String uniformKey, Object uniform){
for(Mesh m : meshes){
if(m.nodeID.equals(meshName)){
m.setUniform(uniformKey, uniform);
}
}
}
}

View File

@ -1,5 +1,6 @@
package electrosphere.renderer;
import electrosphere.renderer.assetmanager.AssetDataStrings;
import electrosphere.renderer.texture.Texture;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
@ -524,10 +525,7 @@ public class ModelUtils {
System.out.println("dimensions: " + (maxX - minX) + "," + (maxY - minY) + "," + (maxZ-minZ));
}
public static Model createBitmapDisplay(
float mStartX, float mStartY, float mWidth, float mHeight,
float tStartX, float tStartY, float tWidth, float tHeight
){
public static Model createBitmapDisplay(){
Model rVal = new Model();
rVal.meshes = new ArrayList();
@ -536,23 +534,23 @@ public class ModelUtils {
glBindVertexArray(m.vertexArrayObject);
//vertices
FloatBuffer VertexArrayBufferData = BufferUtils.createFloatBuffer(12);
VertexArrayBufferData.put( mStartX);
VertexArrayBufferData.put( mStartY+mHeight);
VertexArrayBufferData.put( 0);
VertexArrayBufferData.put( 1);
VertexArrayBufferData.put( mStartX);
VertexArrayBufferData.put( mStartY);
VertexArrayBufferData.put( 0);
VertexArrayBufferData.put( 0);
VertexArrayBufferData.put( mStartX+mWidth);
VertexArrayBufferData.put( mStartY);
VertexArrayBufferData.put( 1);
VertexArrayBufferData.put( 0);
VertexArrayBufferData.put( mStartX);
VertexArrayBufferData.put( mStartY+mHeight);
VertexArrayBufferData.put( 0);
VertexArrayBufferData.put( 1);
VertexArrayBufferData.put( mStartX+mWidth);
VertexArrayBufferData.put( mStartY);
VertexArrayBufferData.put( 1);
VertexArrayBufferData.put( 0);
VertexArrayBufferData.put( mStartX+mWidth);
VertexArrayBufferData.put( mStartY+mHeight);
VertexArrayBufferData.put( 1);
VertexArrayBufferData.put( 1);
VertexArrayBufferData.flip();
@ -570,23 +568,23 @@ public class ModelUtils {
//texture coords
FloatBuffer TextureArrayBufferData = BufferUtils.createFloatBuffer(12);
TextureArrayBufferData.put(tStartX);
TextureArrayBufferData.put(tStartY+tHeight);
TextureArrayBufferData.put(0);
TextureArrayBufferData.put(1);
TextureArrayBufferData.put(tStartX);
TextureArrayBufferData.put(tStartY);
TextureArrayBufferData.put(0);
TextureArrayBufferData.put(0);
TextureArrayBufferData.put(tStartX+tWidth);
TextureArrayBufferData.put(tStartY);
TextureArrayBufferData.put(1);
TextureArrayBufferData.put(0);
TextureArrayBufferData.put(tStartX);
TextureArrayBufferData.put(tStartY+tHeight);
TextureArrayBufferData.put(0);
TextureArrayBufferData.put(1);
TextureArrayBufferData.put(tStartX+tWidth);
TextureArrayBufferData.put(tStartY);
TextureArrayBufferData.put(1);
TextureArrayBufferData.put(0);
TextureArrayBufferData.put(tStartX+tWidth);
TextureArrayBufferData.put(tStartY+tHeight);
TextureArrayBufferData.put(1);
TextureArrayBufferData.put(1);
TextureArrayBufferData.flip();
@ -605,6 +603,7 @@ public class ModelUtils {
glBindVertexArray(0);
m.parent = rVal;
m.nodeID = AssetDataStrings.ASSET_STRING_BITMAP_FONT_MESH_NAME;
Material uiMat = new Material();
Texture uiTex = new Texture("Textures/Fonts/OpenSansBitmap.bmp");

View File

@ -20,6 +20,7 @@ import static electrosphere.main.Main.view_Range;
import electrosphere.renderer.framebuffer.Framebuffer;
import electrosphere.renderer.framebuffer.FramebufferUtils;
import electrosphere.renderer.framebuffer.Renderbuffer;
import electrosphere.renderer.ui.Widget;
import electrosphere.util.Utilities;
import org.joml.Matrix4f;
import org.joml.Vector3f;
@ -192,8 +193,8 @@ public class RenderUtils {
public static void createOpenglContext(){
//Sets the variables that control the window sizing
int screenWidth = 1920;
int screenHeight = 1080;
int screenWidth = Globals.WINDOW_WIDTH;
int screenHeight = Globals.WINDOW_HEIGHT;
//Initializes opengl
glfwInit();
//Gives hints to glfw to control how opengl will be used
@ -509,16 +510,64 @@ public class RenderUtils {
public static void drawScreen(){
//
//first pass: generate depth map
//
if(Globals.RENDER_FLAG_RENDER_SHADOW_MAP){
renderShadowMapFramebufferContent();
}
/*
Render content to the game framebuffer
*/
if(Globals.RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER_CONTENT){
renderGameFramebufferContent();
}
//bind default FBO
glBindFramebuffer(GL_FRAMEBUFFER,0);
glDisable(GL_DEPTH_TEST);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
/*
Render the game framebuffer texture to a quad
*/
if(Globals.RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER){
renderScreenFramebuffer();
}
/*
Render any ui elements
*/
if(Globals.RENDER_FLAG_RENDER_UI){
renderUI();
}
//check and call events and swap the buffers
glfwSwapBuffers(Globals.window);
glfwPollEvents();
}
static void renderShadowMapFramebufferContent(){
Matrix4f modelTransformMatrix = new Matrix4f();
//set the viewport to shadow map size
glViewport(0, 0, 4096, 4096);
glEnable(GL_DEPTH_TEST);
lightDepthBuffer.bind();
glClear(GL_DEPTH_BUFFER_BIT);
//set matrices for light render
float near_plane = 0.001f, far_plane = 100.5f;
Matrix4f lightProjection = new Matrix4f().setOrtho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane);//glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane);
@ -528,17 +577,16 @@ public class RenderUtils {
new Vector3f( 0.0f, 1.0f, 0.0f)
);
Globals.lightDepthMatrix = new Matrix4f(lightProjection).mul(lightView);
// Globals.lightDepthMatrix = new Matrix4f(Globals.projectionMatrix).mul(Globals.viewMatrix);
glUseProgram(lightDepthShaderProgram.shaderProgram);
glUniformMatrix4fv(glGetUniformLocation(lightDepthShaderProgram.shaderProgram, "lightSpaceMatrix"), false, Globals.lightDepthMatrix.get(new float[16]));
// glCullFace(GL_FRONT);
//
// D R A W A L L E N T I T I E S
//
Matrix4f modelTransformMatrix = new Matrix4f();
modelTransformMatrix = new Matrix4f();
for(Entity currentEntity : Globals.entityManager.getDrawable()){
//fetch actor
Actor currentActor = EntityUtils.getEntityActor(currentEntity);
@ -555,24 +603,24 @@ public class RenderUtils {
//draw
currentActor.drawForDepthBuffer();
}
//bind default framebuffer
glBindFramebuffer(GL_FRAMEBUFFER,0);
//reset texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
//reset the viewport to screen size
glViewport(0, 0, 1920, 1080);
glViewport(0, 0, Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT);
//resume culling backface
// glCullFace(GL_BACK);
}
static void renderGameFramebufferContent(){
Matrix4f modelTransformMatrix = new Matrix4f();
//bind screen fbo
screenFramebuffer.bind();
@ -624,17 +672,9 @@ public class RenderUtils {
// glBindVertexArray(0);
//bind default FBO
glBindFramebuffer(GL_FRAMEBUFFER,0);
glDisable(GL_DEPTH_TEST);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
static void renderScreenFramebuffer(){
//
//unbind texture channels
//
@ -662,26 +702,31 @@ public class RenderUtils {
// glBindTexture(GL_TEXTURE_2D, lightDepthBuffer.getTexture());
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
}
static void renderUI(){
Matrix4f modelTransformMatrix = new Matrix4f();
for(Entity currentEntity : Globals.entityManager.getUIElements()){
Actor uiActor = EntityUtils.getEntityActor(currentEntity);
if(currentEntity.getDataKeys().contains(EntityDataStrings.DATA_STRING_UI_ELEMENT_FONT)){
modelTransformMatrix.identity();
modelTransformMatrix.translate(EntityUtils.getEntityPosition(currentEntity));
System.out.println(modelTransformMatrix);
uiActor.applyModelMatrix(modelTransformMatrix);
for(Widget currentWidget : Globals.widgetManager.getWidgetList()){
if(currentWidget.isDraw()){
currentWidget.draw();
}
uiActor.drawUI();
}
// for(Entity currentEntity : Globals.entityManager.getUIElements()){
// Actor uiActor = EntityUtils.getEntityActor(currentEntity);
// if(currentEntity.getDataKeys().contains(EntityDataStrings.DATA_STRING_UI_ELEMENT_FONT)){
// modelTransformMatrix.identity();
// modelTransformMatrix.translate(EntityUtils.getEntityPosition(currentEntity));
// uiActor.applyModelMatrix(modelTransformMatrix);
// }
// uiActor.drawUI();
// }
}
static void renderLoading(){
//check and call events and swap the buffers
glfwSwapBuffers(Globals.window);
glfwPollEvents();
}
}

View File

@ -0,0 +1,10 @@
package electrosphere.renderer.assetmanager;
/**
*
* @author amaterasu
*/
public class AssetDataStrings {
public static final String ASSET_STRING_BITMAP_FONT = "bitmapFont";
public static final String ASSET_STRING_BITMAP_FONT_MESH_NAME = "quad";
}

View File

@ -51,4 +51,8 @@ public class AssetManager {
modelsLoadedIntoMemory.put(rVal,m);
return rVal;
}
public void registerModelToSpecificString(Model m, String s){
modelsLoadedIntoMemory.put(s,m);
}
}

View File

@ -0,0 +1,78 @@
package electrosphere.renderer.ui;
/**
*
* @author amaterasu
*/
public abstract class Widget {
public enum WidgetType {
TEXT_BOX,
}
int width;
int height;
int positionX;
int positionY;
boolean draw;
WidgetType type;
public Widget(int positionX, int positionY, int width, int height, boolean draw, WidgetType type) {
this.width = width;
this.height = height;
this.positionX = positionX;
this.positionY = positionY;
this.draw = draw;
this.type = type;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public int getPositionX() {
return positionX;
}
public int getPositionY() {
return positionY;
}
public boolean isDraw() {
return draw;
}
public void setWidth(int width) {
this.width = width;
}
public void setHeight(int height) {
this.height = height;
}
public void setPositionX(int positionX) {
this.positionX = positionX;
}
public void setPositionY(int positionY) {
this.positionY = positionY;
}
public void setDraw(boolean draw) {
this.draw = draw;
}
public WidgetType getType() {
return type;
}
public abstract void draw();
}

View File

@ -0,0 +1,31 @@
package electrosphere.renderer.ui;
import java.util.HashMap;
import java.util.concurrent.CopyOnWriteArrayList;
/**
*
* @author amaterasu
*/
public class WidgetManager {
CopyOnWriteArrayList<Widget> widgetList = new CopyOnWriteArrayList();
public WidgetManager(){
}
public void registerWidget(Widget w){
if(!widgetList.contains(w)){
widgetList.add(w);
}
}
public CopyOnWriteArrayList<Widget> getWidgetList(){
return widgetList;
}
}

View File

@ -0,0 +1,24 @@
package electrosphere.renderer.ui;
import electrosphere.main.Globals;
import electrosphere.renderer.ui.font.TextBox;
/**
*
* @author amaterasu
*/
public class WidgetUtils {
public static TextBox createTextBox(int positionX, int positionY, int width, int height, int cols, int rows, String text, boolean render){
TextBox rVal = new TextBox(positionX, positionY, width, height, rows, cols, text, render);
Globals.widgetManager.registerWidget(rVal);
return rVal;
}
public static TextBox createVerticallyAlignedTextBox(int width, int positionY, int height, int cols, int rows, String text, boolean render){
TextBox rVal = new TextBox(-width/2, positionY, width/2, height, rows, cols, text, render);
Globals.widgetManager.registerWidget(rVal);
return rVal;
}
}

View File

@ -7,6 +7,8 @@ import electrosphere.main.Globals;
import electrosphere.renderer.ActorUtils;
import electrosphere.renderer.Model;
import electrosphere.renderer.ModelUtils;
import electrosphere.renderer.ui.font.RawFontMap.Glyph;
import java.util.HashMap;
import org.joml.Quaternionf;
import org.joml.Vector3f;
@ -16,19 +18,52 @@ import org.joml.Vector3f;
*/
public class FontUtils {
public static Entity makeFont(int row, int column){
Model letterModel = ModelUtils.createBitmapDisplay(0.0f, 0.0f, 0.0125f, 0.05f, 0.0f + row/8.0f, 0.04f + column/8.0f, 1.0f/16.0f, 1.0f/12.0f);
String managerAssetPath = Globals.assetManager.registerModel(letterModel);
Entity rVal = new Entity();
rVal.putData(EntityDataStrings.DATA_STRING_ACTOR, ActorUtils.createActorFromModelPath(managerAssetPath));
rVal.putData(EntityDataStrings.DATA_STRING_POSITION, new Vector3f(0,0,0));
rVal.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaternionf().rotateAxis((float)0, new Vector3f(1,0,0)));
rVal.putData(EntityDataStrings.DATA_STRING_SCALE, new Vector3f(1,1,1));
rVal.putData(EntityDataStrings.DATA_STRING_UI_ELEMENT, true);
rVal.putData(EntityDataStrings.DATA_STRING_UI_ELEMENT_FONT, true);
Globals.entityManager.registerEntity(rVal);
Globals.entityManager.registerUIEntity(rVal);
return rVal;
static RawFontMap rawFontMap;
static HashMap<Character,Vector3f> positionMap = new HashMap();
static HashMap<Character,Vector3f> dimensionMap = new HashMap();
public static Vector3f getPositionOfCharacter(char character){
Vector3f position;
if((position = positionMap.get(character))!=null){
position = new Vector3f(position);
position.x = position.x / rawFontMap.imageWidth;
position.y = position.y / rawFontMap.imageHeight;
} else {
position = new Vector3f();
}
return position;
}
public static Vector3f getDimensionOfCharacter(char character){
Vector3f dimension;
if((dimension = dimensionMap.get(character))!=null){
dimension = new Vector3f(dimension);
dimension.x = dimension.x / rawFontMap.imageWidth;
dimension.y = dimension.y / rawFontMap.imageHeight;
} else {
dimension = new Vector3f(0.5f,0.5f,0f);
}
return dimension;
}
public static void setFontDataMap(RawFontMap map){
rawFontMap = map;
//fill position map
positionMap.clear();
for(Glyph glyph : rawFontMap.glyphs){
char charVal = glyph.symbol.charAt(0);
Vector3f position = new Vector3f(glyph.startX,glyph.startY,0);
positionMap.put(charVal,position);
}
//fill dimension map
dimensionMap.clear();
for(Glyph glyph : rawFontMap.glyphs){
char charVal = glyph.symbol.charAt(0);
Vector3f dimension = new Vector3f(glyph.width,glyph.height,0);
dimensionMap.put(charVal,dimension);
}
}
}

View File

@ -0,0 +1,27 @@
package electrosphere.renderer.ui.font;
import java.util.HashMap;
import java.util.List;
import org.joml.Vector3f;
/**
*
* @author amaterasu
*/
public class RawFontMap {
public List<Glyph> glyphs;
public int imageWidth;
public int imageHeight;
public class Glyph {
public String symbol;
public int startX;
public int startY;
public int width;
public int height;
}
}

View File

@ -1,41 +1,35 @@
package electrosphere.renderer.ui.font;
import electrosphere.main.Globals;
import electrosphere.renderer.Model;
import electrosphere.renderer.assetmanager.AssetDataStrings;
import electrosphere.renderer.ui.Widget;
import org.joml.Vector3f;
/**
*
* @author amaterasu
*/
public class TextBox {
float ndcX;
float ndcY;
float ndcWidth;
float ndcHeight;
public class TextBox extends Widget{
int positionX;
int positionY;
int width;
int height;
int rows;
int cols;
String text;
public TextBox(float ndcX, float ndcY, float ndcWidth, float ndcHeight, int rows, int cols) {
this.ndcX = ndcX;
this.ndcY = ndcY;
this.ndcWidth = ndcWidth;
this.ndcHeight = ndcHeight;
public TextBox(int positionX, int positionY, int width, int height, int rows, int cols, String text, boolean render) {
super(positionX,positionY,width,height,render,Widget.WidgetType.TEXT_BOX);
this.positionX = positionX;
this.positionY = positionY;
this.width = width;
this.height = height;
this.rows = rows;
this.cols = cols;
}
public float getNdcX() {
return ndcX;
}
public float getNdcY() {
return ndcY;
}
public float getNdcWidth() {
return ndcWidth;
}
public float getNdcHeight() {
return ndcHeight;
this.text = text;
}
public int getRows() {
@ -50,22 +44,6 @@ public class TextBox {
return text;
}
public void setNdcX(float ndcX) {
this.ndcX = ndcX;
}
public void setNdcY(float ndcY) {
this.ndcY = ndcY;
}
public void setNdcWidth(float ndcWidth) {
this.ndcWidth = ndcWidth;
}
public void setNdcHeight(float ndcHeight) {
this.ndcHeight = ndcHeight;
}
public void setRows(int rows) {
this.rows = rows;
}
@ -79,5 +57,36 @@ public class TextBox {
}
@Override
public void draw(){
float ndcX = (float)positionX/Globals.WINDOW_WIDTH;
float ndcY = (float)positionY/Globals.WINDOW_HEIGHT;
float ndcWidth = (float)width/Globals.WINDOW_WIDTH;
float ndcHeight = (float)height/Globals.WINDOW_HEIGHT;
//monowidth for the moment
float charWidth = ndcWidth/cols;
float charHeight = ndcHeight/rows;
for(int y = 0; y < rows; y++){
for(int x = 0; x < cols; x++){
char toDraw = ' ';
if(x + y * cols < text.length()){
toDraw = text.charAt(x + y * cols);
}
Vector3f characterPosition = new Vector3f(ndcX + x * charWidth,ndcY + y * charHeight,0);
Vector3f characterDimensions = new Vector3f(charWidth,charHeight,0);
Vector3f bitMapPosition = FontUtils.getPositionOfCharacter(toDraw);
Vector3f bitMapDimension = FontUtils.getDimensionOfCharacter(toDraw);
Model charModel = Globals.assetManager.fetchModel(AssetDataStrings.ASSET_STRING_BITMAP_FONT);
if(charModel != null){
charModel.pushUniformToMesh(AssetDataStrings.ASSET_STRING_BITMAP_FONT_MESH_NAME, "mPosition", characterPosition);
charModel.pushUniformToMesh(AssetDataStrings.ASSET_STRING_BITMAP_FONT_MESH_NAME, "mDimension", characterDimensions);
charModel.pushUniformToMesh(AssetDataStrings.ASSET_STRING_BITMAP_FONT_MESH_NAME, "tPosition", bitMapPosition);
charModel.pushUniformToMesh(AssetDataStrings.ASSET_STRING_BITMAP_FONT_MESH_NAME, "tDimension", bitMapDimension);
charModel.drawUI();
}
}
}
}
}

View File

@ -1,9 +0,0 @@
package electrosphere.renderer.ui.font;
/**
*
* @author amaterasu
*/
public class TextBoxManager {
}

View File

@ -5,9 +5,24 @@ layout (location = 4) in vec2 aTexCoords;
out vec2 TexCoords;
uniform mat4 model;
uniform vec3 mPosition;
uniform vec3 mDimension;
uniform vec3 tPosition;
uniform vec3 tDimension;
void main(){
gl_Position = model * vec4(aPos.x, aPos.y, 0.0, 1.0);
TexCoords = aTexCoords;
vec2 finalPos = vec2(
aPos.x * mDimension.x + mPosition.x,
aPos.y * mDimension.y + mPosition.y
);
gl_Position = vec4(finalPos.x, finalPos.y, 0.0, 1.0);
vec2 finalTex = vec2(
aTexCoords.x * tDimension.x + tPosition.x,
aTexCoords.y * tDimension.y + tPosition.y
);
TexCoords = finalTex;
}

View File

@ -0,0 +1,209 @@
{
"imageWidth": 256,
"imageHeight": 256,
"glyphs": [
{
"symbol": "A",
"startX": 31,
"startY": 111,
"width": 12,
"height": 14
},
{
"symbol": "B",
"startX": 65,
"startY": 111,
"width": 10,
"height": 14
},
{
"symbol": "C",
"startX": 96,
"startY": 111,
"width": 10,
"height": 14
},
{
"symbol": "D",
"startX": 128,
"startY": 111,
"width": 11,
"height": 14
},
{
"symbol": "E",
"startX": 159,
"startY": 111,
"width": 10,
"height": 14
},
{
"symbol": "F",
"startX": 192,
"startY": 111,
"width": 10,
"height": 14
},
{
"symbol": "G",
"startX": 224,
"startY": 111,
"width": 12,
"height": 13
},
{
"symbol": "O",
"startX": 224,
"startY": 79,
"width": 12,
"height": 14
},
{
"symbol": "N",
"startX": 192,
"startY": 79,
"width": 11,
"height": 14
},
{
"symbol": "M",
"startX": 161,
"startY": 79,
"width": 12,
"height": 14
},
{
"symbol": "L",
"startX": 129,
"startY": 79,
"width": 9,
"height": 14
},
{
"symbol": "K",
"startX": 95,
"startY": 79,
"width": 11,
"height": 13
},
{
"symbol": "J",
"startX": 60,
"startY": 76,
"width": 11,
"height": 16
},
{
"symbol": "I",
"startX": 30,
"startY": 78,
"width": 9,
"height": 14
},
{
"symbol": "H",
"startX": 0,
"startY": 79,
"width": 11,
"height": 13
},
{
"symbol": "W",
"startX": 223,
"startY": 48,
"width": 16,
"height": 12
},
{
"symbol": "X",
"startX": 0,
"startY": 16,
"width": 9,
"height": 11
},
{
"symbol": "V",
"startX": 191,
"startY": 48,
"width": 11,
"height": 12
},
{
"symbol": "U",
"startX": 160,
"startY": 48,
"width": 11,
"height": 12
},
{
"symbol": "T",
"startX": 127,
"startY": 48,
"width": 10,
"height": 12
},
{
"symbol": "S",
"startX": 96,
"startY": 48,
"width": 8,
"height": 13
},
{
"symbol": "R",
"startX": 64,
"startY": 48,
"width": 9,
"height": 13
},
{
"symbol": "Q",
"startX": 32,
"startY": 46,
"width": 12,
"height": 15
},
{
"symbol": "P",
"startX": 0,
"startY": 48,
"width": 9,
"height": 13
},
{
"symbol": "X",
"startX": 0,
"startY": 16,
"width": 10,
"height": 12
},
{
"symbol": "Y",
"startX": 31,
"startY": 16,
"width": 10,
"height": 12
},
{
"symbol": "Z",
"startX": 64,
"startY": 16,
"width": 10,
"height": 12
},
{
"symbol": "[",
"startX": 97,
"startY": 14,
"width": 4,
"height": 13
},
{
"symbol": "\\",
"startX": 128,
"startY": 16,
"width": 6,
"height": 11
}
]
}