From 094831ba22909d444dd52b0254992a1246ed6ee7 Mon Sep 17 00:00:00 2001 From: austin Date: Sun, 23 Apr 2023 11:51:54 -0400 Subject: [PATCH] Initial marching cubes! --- assets/Scenes/testscene1/testscene1.json | 22 - .../electrosphere/engine/LoadingThread.java | 4 + .../engine/assetmanager/AssetManager.java | 10 + .../electrosphere/entity/EntityUtils.java | 2 +- .../entity/types/terrain/TerrainChunk.java | 91 +++ .../game/client/ClientFunctions.java | 2 + .../client/terrain/cache/LoadingChunk.java | 4 + .../terrain/manager/ClientTerrainManager.java | 28 +- .../manager/TerrainChunkGenQueueItem.java | 21 + .../java/electrosphere/renderer/Mesh.java | 4 + .../java/electrosphere/renderer/Model.java | 6 +- .../renderer/actor/ActorUtils.java | 5 + .../meshgen/TerrainChunkModelGeneration.java | 673 ++++++++++++++++++ 13 files changed, 847 insertions(+), 25 deletions(-) create mode 100644 src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java create mode 100644 src/main/java/electrosphere/game/client/terrain/manager/TerrainChunkGenQueueItem.java create mode 100644 src/main/java/electrosphere/renderer/meshgen/TerrainChunkModelGeneration.java diff --git a/assets/Scenes/testscene1/testscene1.json b/assets/Scenes/testscene1/testscene1.json index d6c7cd61..c4c43074 100644 --- a/assets/Scenes/testscene1/testscene1.json +++ b/assets/Scenes/testscene1/testscene1.json @@ -1,27 +1,5 @@ { "entities": [ - { - "type": "object", - "subtype": "terrain1", - "posX": 2, - "posY": 1, - "posZ": 2, - "rotX": -0.7071068, - "rotY": 0, - "rotZ": 0, - "rotW": 0.7071068 - }, - { - "type": "object", - "subtype": "skyscraper1", - "posX": 2, - "posY": 1, - "posZ": 2, - "rotX": -0.7071068, - "rotY": 0, - "rotZ": 0, - "rotW": 0.7071068 - } ], "scriptPaths": [ "Scenes/testscene1/someScript.js" diff --git a/src/main/java/electrosphere/engine/LoadingThread.java b/src/main/java/electrosphere/engine/LoadingThread.java index e9f8cf93..429924f8 100644 --- a/src/main/java/electrosphere/engine/LoadingThread.java +++ b/src/main/java/electrosphere/engine/LoadingThread.java @@ -24,6 +24,7 @@ import electrosphere.entity.types.creature.CreatureTemplate; import electrosphere.entity.types.creature.CreatureUtils; import electrosphere.entity.types.item.ItemUtils; import electrosphere.entity.types.object.ObjectUtils; +import electrosphere.entity.types.terrain.TerrainChunk; import electrosphere.game.client.cells.DrawCellManager; import electrosphere.game.client.targeting.crosshair.Crosshair; import electrosphere.game.client.terrain.manager.ClientTerrainManager; @@ -777,6 +778,9 @@ public class LoadingThread extends Thread { SceneLoader loader = new SceneLoader(); loader.serverInstantiateSceneFile("Scenes/testscene1/testscene1.json"); + + Entity chunk = TerrainChunk.createTerrainChunkEntity(); + // Globals.entityManager.registerBehaviorTree(new BehaviorTree() { // int i = 0; // public void simulate(){ diff --git a/src/main/java/electrosphere/engine/assetmanager/AssetManager.java b/src/main/java/electrosphere/engine/assetmanager/AssetManager.java index a2a83c16..c617a918 100644 --- a/src/main/java/electrosphere/engine/assetmanager/AssetManager.java +++ b/src/main/java/electrosphere/engine/assetmanager/AssetManager.java @@ -123,6 +123,16 @@ public class AssetManager { modelsLoadedIntoMemory.put(rVal,m); return rVal; } + + /** + * Registers a (presumably generated in code) model to a given path in the asset manager + * Used particularly if you have a specific path you want to relate to a specific model (eg terrain chunk code) + * @param m The model to register + * @param path The path to register the model to + */ + public void registerModelWithPath(Model m, String path){ + modelsLoadedIntoMemory.put(path, m); + } public void registerModelToSpecificString(Model m, String s){ modelsLoadedIntoMemory.put(s,m); diff --git a/src/main/java/electrosphere/entity/EntityUtils.java b/src/main/java/electrosphere/entity/EntityUtils.java index 0a90891f..b63effff 100644 --- a/src/main/java/electrosphere/entity/EntityUtils.java +++ b/src/main/java/electrosphere/entity/EntityUtils.java @@ -58,7 +58,7 @@ public class EntityUtils { public static Entity spawnDrawableEntityWithPreexistingModel(String modelPath){ Entity rVal = new Entity(); - rVal.putData(EntityDataStrings.DATA_STRING_ACTOR, ActorUtils.createActorFromModelPath(modelPath)); + rVal.putData(EntityDataStrings.DATA_STRING_ACTOR, ActorUtils.createActorOfLoadingModel(modelPath)); rVal.putData(EntityDataStrings.DATA_STRING_POSITION, new Vector3d(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)); diff --git a/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java b/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java new file mode 100644 index 00000000..998991ea --- /dev/null +++ b/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java @@ -0,0 +1,91 @@ +package electrosphere.entity.types.terrain; + +import electrosphere.engine.Globals; +import electrosphere.entity.Entity; +import electrosphere.entity.EntityUtils; +import electrosphere.renderer.Model; +import electrosphere.renderer.meshgen.TerrainChunkModelGeneration; + +public class TerrainChunk { + + + public static Entity createTerrainChunkEntity(){ + + float[][][] terrainGrid = new float[][][]{ + //plane 1 + { + //row 1 + {-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,}, + //row 2 + {-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,}, + //row 3 + {-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,}, + //row 4 + {-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,}, + //row 5 + {-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,}, + }, + //plane 2 + { + //row 1 + {-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,}, + //row 2 + {-1.0f,1.0f,1.0f,1.0f,-1.0f,}, + //row 3 + {-1.0f,1.0f,1.0f,1.0f,-1.0f,}, + //row 4 + {-1.0f,1.0f,1.0f,1.0f,-1.0f,}, + //row 5 + {-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,}, + }, + //plane 3 + { + //row 1 + {-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,}, + //row 2 + {-1.0f,1.0f,1.0f,1.0f,-1.0f,}, + //row 3 + {-1.0f,1.0f,1.0f,1.0f,-1.0f,}, + //row 4 + {-1.0f,1.0f,1.0f,1.0f,-1.0f,}, + //row 5 + {-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,}, + }, + //plane 4 + { + //row 1 + {-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,}, + //row 2 + {-1.0f,1.0f,1.0f,1.0f,-1.0f,}, + //row 3 + {-1.0f,1.0f,1.0f,1.0f,-1.0f,}, + //row 4 + {-1.0f,1.0f,1.0f,1.0f,-1.0f,}, + //row 5 + {-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,}, + }, + //plane 5 + { + //row 1 + {-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,}, + //row 2 + {-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,}, + //row 3 + {-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,}, + //row 4 + {-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,}, + //row 5 + {-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,}, + }, + }; + + String modelPath = Globals.clientTerrainManager.queueTerrainGridGeneration(terrainGrid); + + Entity rVal = EntityUtils.spawnDrawableEntityWithPreexistingModel(modelPath); + + EntityUtils.getPosition(rVal).set(1,-2,1); + + return rVal; + } + +} diff --git a/src/main/java/electrosphere/game/client/ClientFunctions.java b/src/main/java/electrosphere/game/client/ClientFunctions.java index 7e61f1c1..4d071cdc 100644 --- a/src/main/java/electrosphere/game/client/ClientFunctions.java +++ b/src/main/java/electrosphere/game/client/ClientFunctions.java @@ -3,6 +3,7 @@ package electrosphere.game.client; import electrosphere.engine.Globals; import electrosphere.entity.EntityUtils; import electrosphere.entity.types.camera.CameraEntityUtils; +import electrosphere.game.client.terrain.manager.ClientTerrainManager; import org.joml.Vector3d; import org.joml.Vector3f; @@ -30,6 +31,7 @@ public class ClientFunctions { Globals.clientTerrainManager.handleMessages(); Globals.clientTerrainManager.ejectLoadedChunks(); } + ClientTerrainManager.generateTerrainChunkGeometry(); updateSkyboxPos(); updateCellManager(); } diff --git a/src/main/java/electrosphere/game/client/terrain/cache/LoadingChunk.java b/src/main/java/electrosphere/game/client/terrain/cache/LoadingChunk.java index 868d49d2..eb5487a1 100644 --- a/src/main/java/electrosphere/game/client/terrain/cache/LoadingChunk.java +++ b/src/main/java/electrosphere/game/client/terrain/cache/LoadingChunk.java @@ -73,6 +73,10 @@ public class LoadingChunk { public int getWorldY() { return worldY; } + + public ClientWorldData getClientWorldData(){ + return this.clientWorldData; + } diff --git a/src/main/java/electrosphere/game/client/terrain/manager/ClientTerrainManager.java b/src/main/java/electrosphere/game/client/terrain/manager/ClientTerrainManager.java index 8feddbe0..ff0ddfa0 100644 --- a/src/main/java/electrosphere/game/client/terrain/manager/ClientTerrainManager.java +++ b/src/main/java/electrosphere/game/client/terrain/manager/ClientTerrainManager.java @@ -1,13 +1,19 @@ package electrosphere.game.client.terrain.manager; +import electrosphere.engine.Globals; import electrosphere.game.client.terrain.cache.ClientTerrainCache; import electrosphere.game.client.terrain.cache.LoadingChunk; import electrosphere.game.client.terrain.cache.LoadingChunkCache; import electrosphere.game.client.world.ClientWorldData; import electrosphere.logger.LoggerInterface; import electrosphere.net.parser.net.message.TerrainMessage; +import electrosphere.renderer.Model; +import electrosphere.renderer.meshgen.TerrainChunkModelGeneration; + +import java.util.Collections; import java.util.LinkedList; import java.util.List; +import java.util.UUID; import java.util.concurrent.CopyOnWriteArrayList; public class ClientTerrainManager { @@ -27,6 +33,8 @@ public class ClientTerrainManager { ClientWorldData clientWorldData; + + static List terrainChunkGenerationQueue = Collections.synchronizedList(new LinkedList()); public ClientTerrainManager(ClientWorldData clientWorldData){ @@ -200,7 +208,7 @@ public class ClientTerrainManager { public void ejectLoadedChunks(){ List chunksToEject = new LinkedList(); for(LoadingChunk chunk : loadingChunkCache.getChunks()){ - if(chunk.isComplete()){ + if(chunk.isComplete() && chunk.getClientWorldData() != null){ float[][] heightMap = chunk.exportFloats(); terrainCache.addFloatsToCache(chunk.getWorldX(), chunk.getWorldY(), heightMap); chunksToEject.add(chunk); @@ -210,5 +218,23 @@ public class ClientTerrainManager { loadingChunkCache.remove(loadedChunk); } } + + public static String queueTerrainGridGeneration(float[][][] terrainGrid){ + String promisedHash = ""; + UUID newUUID = UUID.randomUUID(); + promisedHash = newUUID.toString(); + TerrainChunkGenQueueItem queueItem = new TerrainChunkGenQueueItem(terrainGrid, promisedHash); + terrainChunkGenerationQueue.add(queueItem); + return promisedHash; + } + + public static void generateTerrainChunkGeometry(){ + for(TerrainChunkGenQueueItem queueItem : terrainChunkGenerationQueue){ + System.out.println(queueItem); + Model terrainModel = TerrainChunkModelGeneration.generateTerrainModel(queueItem.getTerrainGrid()); + Globals.assetManager.registerModelToSpecificString(terrainModel, queueItem.getPromisedHash()); + } + terrainChunkGenerationQueue.clear(); + } } diff --git a/src/main/java/electrosphere/game/client/terrain/manager/TerrainChunkGenQueueItem.java b/src/main/java/electrosphere/game/client/terrain/manager/TerrainChunkGenQueueItem.java new file mode 100644 index 00000000..27a16bb5 --- /dev/null +++ b/src/main/java/electrosphere/game/client/terrain/manager/TerrainChunkGenQueueItem.java @@ -0,0 +1,21 @@ +package electrosphere.game.client.terrain.manager; + +public class TerrainChunkGenQueueItem { + + float[][][] terrainGrid; + String promisedHash; + + public TerrainChunkGenQueueItem(float[][][] terrainGrid, String promisedHash){ + this.terrainGrid = terrainGrid; + this.promisedHash = promisedHash; + } + + public float[][][] getTerrainGrid(){ + return this.terrainGrid; + } + + public String getPromisedHash(){ + return this.promisedHash; + } + +} diff --git a/src/main/java/electrosphere/renderer/Mesh.java b/src/main/java/electrosphere/renderer/Mesh.java index 6a6df571..b0233dd0 100644 --- a/src/main/java/electrosphere/renderer/Mesh.java +++ b/src/main/java/electrosphere/renderer/Mesh.java @@ -516,6 +516,10 @@ public class Mesh { public void setMaterial(Material input){ this.material = input; } + + public void setShader(ShaderProgram shader){ + this.shader = shader; + } diff --git a/src/main/java/electrosphere/renderer/Model.java b/src/main/java/electrosphere/renderer/Model.java index ac3e77a8..a40b545f 100644 --- a/src/main/java/electrosphere/renderer/Model.java +++ b/src/main/java/electrosphere/renderer/Model.java @@ -53,7 +53,7 @@ import org.lwjgl.assimp.AINode; public class Model { boolean is_Ready_To_Display = false; AIScene scene; - public ArrayList meshes; + public List meshes; public ArrayList animations; public ArrayList bones; public HashMap boneMap; @@ -519,4 +519,8 @@ public class Model { public void setTextureMask(Map textureMask){ this.textureMap = textureMask; } + + public void setProgram(ShaderProgram program){ + this.program = program; + } } diff --git a/src/main/java/electrosphere/renderer/actor/ActorUtils.java b/src/main/java/electrosphere/renderer/actor/ActorUtils.java index 171db457..73531ff1 100644 --- a/src/main/java/electrosphere/renderer/actor/ActorUtils.java +++ b/src/main/java/electrosphere/renderer/actor/ActorUtils.java @@ -16,6 +16,11 @@ public class ActorUtils { Globals.assetManager.addModelPathToQueue(modelPath); return rVal; } + + public static Actor createActorOfLoadingModel(String modelPath){ + Actor rVal = new Actor(modelPath); + return rVal; + } public static void applyBlenderTransformer(Entity actorEntity){ Actor entityActor = EntityUtils.getActor(actorEntity); diff --git a/src/main/java/electrosphere/renderer/meshgen/TerrainChunkModelGeneration.java b/src/main/java/electrosphere/renderer/meshgen/TerrainChunkModelGeneration.java new file mode 100644 index 00000000..132a59ff --- /dev/null +++ b/src/main/java/electrosphere/renderer/meshgen/TerrainChunkModelGeneration.java @@ -0,0 +1,673 @@ +package electrosphere.renderer.meshgen; + +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.joml.Matrix4f; +import org.joml.Vector3f; +import org.lwjgl.BufferUtils; + + + +import static org.lwjgl.opengl.GL30.glBindVertexArray; +import static org.lwjgl.opengl.GL30.glGenVertexArrays; + +import electrosphere.engine.Globals; +import electrosphere.renderer.Material; +import electrosphere.renderer.Mesh; +import electrosphere.renderer.Model; + +public class TerrainChunkModelGeneration { + + //http://paulbourke.net/geometry/polygonise/ + + static int edgeTable[]={ + 0x0 , 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, + 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, + 0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, + 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, + 0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, + 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, + 0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, + 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, + 0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, + 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, + 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, + 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, + 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, + 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, + 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , + 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, + 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, + 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, + 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, + 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, + 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, + 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, + 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, + 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460, + 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, + 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0, + 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, + 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230, + 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, + 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190, + 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, + 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 + }; + + //256 by 16 + static int triTable[][] = { + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1}, + {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, + {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, + {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1}, + {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1}, + {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1}, + {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1}, + {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1}, + {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1}, + {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, + {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1}, + {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1}, + {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1}, + {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1}, + {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1}, + {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1}, + {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1}, + {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1}, + {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1}, + {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1}, + {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1}, + {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1}, + {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1}, + {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1}, + {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, + {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1}, + {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1}, + {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1}, + {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1}, + {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1}, + {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1}, + {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1}, + {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1}, + {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1}, + {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1}, + {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1}, + {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1}, + {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1}, + {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1}, + {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1}, + {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1}, + {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1}, + {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1}, + {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1}, + {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1}, + {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1}, + {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1}, + {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1}, + {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1}, + {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1}, + {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1}, + {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1}, + {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1}, + {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1}, + {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1}, + {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1}, + {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1}, + {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1}, + {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1}, + {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1}, + {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1}, + {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1}, + {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1}, + {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1}, + {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1}, + {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, + {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1}, + {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, + {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1}, + {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1}, + {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1}, + {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1}, + {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1}, + {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1}, + {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1}, + {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1}, + {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1}, + {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1}, + {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1}, + {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1}, + {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1}, + {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1}, + {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1}, + {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1}, + {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1}, + {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1}, + {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1}, + {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1}, + {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1}, + {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1}, + {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1}, + {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1}, + {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1}, + {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1}, + {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1}, + {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1}, + {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1}, + {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1}, + {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1}, + {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1}, + {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1}, + {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1}, + {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1}, + {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1}, + {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1}, + {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1}, + {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1}, + {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1}, + {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1}, + {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1}, + {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1}, + {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1}, + {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1}, + {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1}, + {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1}, + {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1}, + {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1}, + {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1}, + {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1}, + {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1}, + {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1}, + {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1}, + {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1}, + {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1}, + {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1}, + {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} + }; + + static class Triangle { + Vector3f[] points = new Vector3f[3]; //array of size 3 + + public Triangle(Vector3f point0,Vector3f point1,Vector3f point2){ + points[0] = point0; + points[1] = point1; + points[2] = point2; + } + } + + static class GridCell { + Vector3f[] points = new Vector3f[8]; //array of size 8 + double[] val = new double[8]; //array of size 8 + public void setValues( + Vector3f p1, Vector3f p2, Vector3f p3, Vector3f p4, + Vector3f p5, Vector3f p6, Vector3f p7, Vector3f p8, + double val1, double val2, double val3, double val4, + double val5, double val6, double val7, double val8 + ){ + points[0] = p1; points[1] = p2; points[2] = p3; points[3] = p4; + points[4] = p5; points[5] = p6; points[6] = p7; points[7] = p8; + val[0] = val1; val[1] = val2; val[2] = val3; val[3] = val4; + val[4] = val5; val[5] = val6; val[6] = val7; val[7] = val8; + } + } + + protected static int polygonize(GridCell grid, double isolevel, List triangles, Map vertMap, List indices){ + int i; + int ntriang; + int cubeIndex = 0; + Vector3f[] vertList = new Vector3f[12]; + + //get lookup key (index) for edge table + //edge table tells us which vertices are inside of the surface + if (grid.val[0] < isolevel) cubeIndex |= 1; + if (grid.val[1] < isolevel) cubeIndex |= 2; + if (grid.val[2] < isolevel) cubeIndex |= 4; + if (grid.val[3] < isolevel) cubeIndex |= 8; + if (grid.val[4] < isolevel) cubeIndex |= 16; + if (grid.val[5] < isolevel) cubeIndex |= 32; + if (grid.val[6] < isolevel) cubeIndex |= 64; + if (grid.val[7] < isolevel) cubeIndex |= 128; + + //Cube is entirely in/out of the surface + if (edgeTable[cubeIndex] == 0) + return(0); + + //instead of having all intersections be perfectly at the midpoint, + //for each edge this code calculates where along the edge to place the vertex + //this should dramatically smooth the surface + if ((edgeTable[cubeIndex] & 1) > 0) + vertList[0] = + VertexInterp(isolevel,grid.points[0],grid.points[1],grid.val[0],grid.val[1]); + if ((edgeTable[cubeIndex] & 2) > 0) + vertList[1] = + VertexInterp(isolevel,grid.points[1],grid.points[2],grid.val[1],grid.val[2]); + if ((edgeTable[cubeIndex] & 4) > 0) + vertList[2] = + VertexInterp(isolevel,grid.points[2],grid.points[3],grid.val[2],grid.val[3]); + if ((edgeTable[cubeIndex] & 8) > 0) + vertList[3] = + VertexInterp(isolevel,grid.points[3],grid.points[0],grid.val[3],grid.val[0]); + if ((edgeTable[cubeIndex] & 16) > 0) + vertList[4] = + VertexInterp(isolevel,grid.points[4],grid.points[5],grid.val[4],grid.val[5]); + if ((edgeTable[cubeIndex] & 32) > 0) + vertList[5] = + VertexInterp(isolevel,grid.points[5],grid.points[6],grid.val[5],grid.val[6]); + if ((edgeTable[cubeIndex] & 64) > 0) + vertList[6] = + VertexInterp(isolevel,grid.points[6],grid.points[7],grid.val[6],grid.val[7]); + if ((edgeTable[cubeIndex] & 128) > 0) + vertList[7] = + VertexInterp(isolevel,grid.points[7],grid.points[4],grid.val[7],grid.val[4]); + if ((edgeTable[cubeIndex] & 256) > 0) + vertList[8] = + VertexInterp(isolevel,grid.points[0],grid.points[4],grid.val[0],grid.val[4]); + if ((edgeTable[cubeIndex] & 512) > 0) + vertList[9] = + VertexInterp(isolevel,grid.points[1],grid.points[5],grid.val[1],grid.val[5]); + if ((edgeTable[cubeIndex] & 1024) > 0) + vertList[10] = + VertexInterp(isolevel,grid.points[2],grid.points[6],grid.val[2],grid.val[6]); + if ((edgeTable[cubeIndex] & 2048) > 0) + vertList[11] = + VertexInterp(isolevel,grid.points[3],grid.points[7],grid.val[3],grid.val[7]); + + //Create the triangle + ntriang = 0; + for (i=0; triTable[cubeIndex][i]!=-1; i+=3) { + Vector3f vert0 = vertList[triTable[cubeIndex][i+0]]; + Vector3f vert1 = vertList[triTable[cubeIndex][i+1]]; + Vector3f vert2 = vertList[triTable[cubeIndex][i+2]]; + getVertIndex(vert0,vertMap,indices); + getVertIndex(vert1,vertMap,indices); + getVertIndex(vert2,vertMap,indices); + Triangle newTriangle = new Triangle( + vertList[triTable[cubeIndex][i+0]], + vertList[triTable[cubeIndex][i+1]], + vertList[triTable[cubeIndex][i+2]] + ); + triangles.add(newTriangle); + // triangles[ntriang].points[0] = vertList[triTable[cubeIndex][i+0]]; + // triangles[ntriang].points[1] = vertList[triTable[cubeIndex][i+1]]; + // triangles[ntriang].points[2] = vertList[triTable[cubeIndex][i+2]]; + ntriang++; + } + + return(ntriang); + } + + //interpolates the location that the edge gets cut based on the magnitudes of the scalars of the vertices at either end of the edge + static Vector3f VertexInterp(double isolevel, Vector3f p1, Vector3f p2, double valp1, double valp2){ + double mu; + float x, y, z; + + if (Math.abs(isolevel-valp1) < 0.00001) + return(p1); + if (Math.abs(isolevel-valp2) < 0.00001) + return(p2); + if (Math.abs(valp1-valp2) < 0.00001) + return(p1); + mu = (isolevel - valp1) / (valp2 - valp1); + x = (float)(p1.x + mu * (p2.x - p1.x)); + y = (float)(p1.y + mu * (p2.y - p1.y)); + z = (float)(p1.z + mu * (p2.z - p1.z)); + + return new Vector3f(x,y,z); + } + + + protected static Mesh generateTerrainMesh(float[][][] terrainGrid){ + + Mesh mesh = new Mesh(); + + + mesh.mesh = null; + + // + // VAO + // + mesh.vertexArrayObject = glGenVertexArrays(); + glBindVertexArray(mesh.vertexArrayObject); + + // 5 6 + // +-------------+ +-----5-------+ + // / | / | / | /| + // / | / | 4 9 6 10 + // 4 +-----+-------+ 7 | +-----+7------+ | + // | 1 +-------+-----+ 2 | +-----1-+-----+ + // | / | / 8 0 11 2 + // | / | / | / | / + // 0 +-------------+ 3 +------3------+ + + GridCell currentCell = new GridCell(); + List triangles = new LinkedList(); + Map vertMap = new HashMap(); + List indices = new LinkedList(); + for(int x = 0; x < terrainGrid.length - 1; x++){ + for(int y = 0; y < terrainGrid[0].length - 1; y++){ + for(int z = 0; z < terrainGrid[0][0].length - 1; z++){ + //push the current cell's values into the gridcell + currentCell.setValues( + new Vector3f(x+0,y+0,z+0), new Vector3f(x+0,y+0,z+1), new Vector3f(x+1,y+0,z+1), new Vector3f(x+1,y+0,z+0), + new Vector3f(x+0,y+1,z+0), new Vector3f(x+0,y+1,z+1), new Vector3f(x+1,y+1,z+1), new Vector3f(x+1,y+1,z+0), + terrainGrid[x+0][y+0][z+0], terrainGrid[x+0][y+0][z+1], terrainGrid[x+1][y+0][z+1], terrainGrid[x+1][y+0][z+0], + terrainGrid[x+0][y+1][z+0], terrainGrid[x+0][y+1][z+1], terrainGrid[x+1][y+1][z+1], terrainGrid[x+1][y+1][z+0] + ); + //polygonize the current gridcell + polygonize(currentCell, 0, triangles, vertMap, indices); + } + } + } + + + + + + // + //Buffer data to GPU + // + + try { + mesh.vertexCount = triangles.size() * 3; + FloatBuffer VertexArrayBufferData = BufferUtils.createFloatBuffer(mesh.vertexCount * 3); + float[] temp = new float[3]; + for(Triangle triangle: triangles){ + //vertex 1 + temp[0] = triangle.points[0].x; + temp[1] = triangle.points[0].y; + temp[2] = triangle.points[0].z; + VertexArrayBufferData.put(temp); + //vertex 2 + temp[0] = triangle.points[1].x; + temp[1] = triangle.points[1].y; + temp[2] = triangle.points[1].z; + VertexArrayBufferData.put(temp); + //vertex 3 + temp[0] = triangle.points[2].x; + temp[1] = triangle.points[2].y; + temp[2] = triangle.points[2].z; + VertexArrayBufferData.put(temp); + } + VertexArrayBufferData.flip(); + mesh.buffer_vertices(VertexArrayBufferData, 3); + } catch (NullPointerException ex){ + ex.printStackTrace(); + } + + + + // + // FACES + // + mesh.faceCount = triangles.size(); + mesh.elementCount = triangles.size() * 3; + try { + IntBuffer elementArrayBufferData = BufferUtils.createIntBuffer(mesh.elementCount); + int i = 0; + int[] temp = new int[3]; + for(Triangle triangle : triangles){ + temp[0] = i * 3 + 0; + temp[1] = i * 3 + 1; + temp[2] = i * 3 + 2; + i++; + elementArrayBufferData.put(temp); + } + elementArrayBufferData.flip(); + mesh.buffer_faces(elementArrayBufferData); + } catch (NullPointerException ex){ + ex.printStackTrace(); + } + + + + + // + // NORMALS + // + try { + mesh.normalCount = triangles.size() * 3; + FloatBuffer NormalArrayBufferData; + if(mesh.normalCount > 0){ + NormalArrayBufferData = BufferUtils.createFloatBuffer(mesh.normalCount * 3); + float[] temp = new float[3]; + for(Triangle triangle : triangles){ + //calculate normal vector + Vector3f u = triangle.points[1].sub(triangle.points[0], new Vector3f()); + Vector3f v = triangle.points[2].sub(triangle.points[1], new Vector3f()); + Vector3f n = new Vector3f(u.y * v.z - u.z * v.y, u.z * v.x - u.x * v.z, u.x * v.y - u.y * v.x); + //emit 3 normal vectors + //normal 0 + temp[0] = n.x; + temp[1] = n.y; + temp[2] = n.z; + NormalArrayBufferData.put(temp); + //normal 1 + temp[0] = n.x; + temp[1] = n.y; + temp[2] = n.z; + NormalArrayBufferData.put(temp); + //normal 2 + temp[0] = n.x; + temp[1] = n.y; + temp[2] = n.z; + NormalArrayBufferData.put(temp); + } + NormalArrayBufferData.flip(); + mesh.buffer_normals(NormalArrayBufferData, 3); + } + } catch (NullPointerException ex){ + ex.printStackTrace(); + } + + // + // TEXTURE COORDINATES + // + /*try { + skyboxmesh.textureCoordCount = mesh.mTextureCoords(0).capacity(); + FloatBuffer TextureArrayBufferData; + if(skyboxmesh.textureCoordCount > 0){ + TextureArrayBufferData = BufferUtils.createFloatBuffer(skyboxmesh.textureCoordCount * 2); + float[] temp = new float[2]; + for (int i = 0; i < skyboxmesh.textureCoordCount; i++) { + AIVector3D normal = texturecoords.get(i); + temp[0] = normal.x(); + temp[1] = normal.y(); +// temp[2] = normal.z(); + TextureArrayBufferData.put(temp); + } + TextureArrayBufferData.flip(); + skyboxmesh.buffer_texture_coords(TextureArrayBufferData); + } + } catch (NullPointerException ex){ + ex.printStackTrace(); + } + + skyboxmesh.shader = ShaderProgram.smart_assemble_shader(has_bones, apply_lighting); + + skybox_model.materials.add(optionalMaterial); + */ + + + + + + glBindVertexArray(0); + mesh.nodeID = "terrainChunk"; + return mesh; + } + + + + public static Model generateTerrainModel(float[][][] terrainGrid){ + Model rVal = new Model(); + rVal.meshes = new ArrayList(); + Mesh m = generateTerrainMesh(terrainGrid); + + + Material groundMat = new Material(); + groundMat.set_diffuse("/Textures/Ground/Dirt1.png"); + groundMat.set_specular("/Textures/Ground/Dirt1.png"); + m.setMaterial(groundMat); + + m.setShader(Globals.defaultMeshShader); + m.parent = rVal; + + rVal.meshes.add(m); + + return rVal; + } + + private static String getVertKeyFromPoints(float x, float y, float z){ + return x + "-" + y + "-" + z; + } + + private static int getVertIndex(Vector3f vert, Map vertMap, List indices){ + int rVal = -1; + String vertKey = getVertKeyFromPoints(vert.x,vert.y,vert.z); + if(vertMap.containsKey(vertKey)){ + return vertMap.get(vertKey); + } else { + rVal = indices.size(); + indices.add(rVal); + vertMap.put(vertKey,rVal); + return rVal; + } + } + +}