diff --git a/buildNumber.properties b/buildNumber.properties index 14c7a866..147fbfe3 100644 --- a/buildNumber.properties +++ b/buildNumber.properties @@ -1,3 +1,3 @@ #maven.buildNumber.plugin properties file -#Fri Jun 14 13:45:11 EDT 2024 -buildNumber=137 +#Wed Jun 19 19:19:09 EDT 2024 +buildNumber=138 diff --git a/docs/src/architecture/architectureindex.md b/docs/src/architecture/architectureindex.md index dc28fc1a..f5717514 100644 --- a/docs/src/architecture/architectureindex.md +++ b/docs/src/architecture/architectureindex.md @@ -10,6 +10,7 @@ - @subpage archimprovementtargets - @subpage savesindex - @subpage hitboxesindex +- @subpage drawcell # What is this section diff --git a/docs/src/architecture/drawcell/drawcell.md b/docs/src/architecture/drawcell/drawcell.md new file mode 100644 index 00000000..05d42f61 --- /dev/null +++ b/docs/src/architecture/drawcell/drawcell.md @@ -0,0 +1,6 @@ +@page drawcell Draw Cells + +[TOC] + - @subpage transvoxelalgorithm + +![](/docs/src/images/architecture/drawcell/ClientMacroArch.png) \ No newline at end of file diff --git a/docs/src/architecture/drawcell/transvoxelalgorithm.md b/docs/src/architecture/drawcell/transvoxelalgorithm.md new file mode 100644 index 00000000..e0a8002e --- /dev/null +++ b/docs/src/architecture/drawcell/transvoxelalgorithm.md @@ -0,0 +1,49 @@ +@page transvoxelalgorithm Transvoxel Chunk Generation + + + +# High Level + + +The goal of the transvoxel algorithm is to bridge the divide between a chunk of a higher resolution and a chunk of a lower resolution + +![](/docs/src/images/architecture/drawcell/completevoxel.png) + +For the voxels on the border between a low resolution chunk and a high resolution chunk, we split the voxels in half + +![](/docs/src/images/architecture/drawcell/bisectedvoxel.png) + +On the low-resolution half, we perform marching cubes + +On the high resolution half, we generate a mesh that adapts the high resolution chunk to the low resolution voxel + +![](/docs/src/images/architecture/drawcell/adaptedvoxel.png) + +# Major files +[TransvoxelModelGeneration.java](@ref #electrosphere.renderer.meshgen.TransvoxelModelGeneration) - The main class that turns voxel data into mesh data + +[TerrainChunk.java](@ref #electrosphere.entity.types.terrain.TerrainChunk) - The class that the DrawCellManager calls to generate chunk meshes + +[ClientTerrainManager.java](@ref #electrosphere.client.terrain.manager.ClientTerrainManager) - Handles queueing the mesh data to the gpu + +[TerrainChunkGenQueueItem.java](@ref #electrosphere.client.terrain.manager.TerrainChunkGenQueueItem) - A terrain mesh that has been queued to be added to the gpu + + + + +# Implementation Notes + +The description of the algorithm always refers to the transition cells in terms of y pointing upwards and x pointing to the right. +This can be confusing to think about how it would apply to other orientations (ie what if we're working along the y axis or something and x is "up"). +All these transforms are done implicitly in the `generateTerrainChunkData` function. When iterating across each axis, I've manually calculated what point should be where. +The inner polygonize functions always treat it as y-up, but that works because this transform has already been done before the data is passed in. + + + + +# Notes about table format + - `transitionCellClass` is indexed into by the case index value generated from the high resolution face data + - `transitionCellData` is indexed into by the class value from the `transitionCellClass` + - `transitionVertexData` is ALSO indexed into by the CASE INDEX VALUE, NOT THE CLASS VALUE. You can figure this out because the `transitionVertexData` has 512 entries + + diff --git a/docs/src/highlevel-design/narrativemanager/narrativearcdesign.md b/docs/src/highlevel-design/narrativemanager/narrativearcdesign.md new file mode 100644 index 00000000..92c38791 --- /dev/null +++ b/docs/src/highlevel-design/narrativemanager/narrativearcdesign.md @@ -0,0 +1,3 @@ +@page narrativearcdesign Narrative Arc Design + +Use the idea of conspiracy to create mystery eventually leading to a big series of confrontations and narrative payoff \ No newline at end of file diff --git a/docs/src/highlevel-design/narrativemanager/narrativemanager.md b/docs/src/highlevel-design/narrativemanager/narrativemanager.md index bc206cff..6aa9fcea 100644 --- a/docs/src/highlevel-design/narrativemanager/narrativemanager.md +++ b/docs/src/highlevel-design/narrativemanager/narrativemanager.md @@ -4,6 +4,7 @@ [TOC] - @subpage whatmakesaquestgood + - @subpage narrativearcdesign TODO: describe diff --git a/docs/src/images/architecture/drawcell/ClientMacroArch.png b/docs/src/images/architecture/drawcell/ClientMacroArch.png new file mode 100644 index 00000000..9c5691d2 Binary files /dev/null and b/docs/src/images/architecture/drawcell/ClientMacroArch.png differ diff --git a/docs/src/images/architecture/drawcell/adaptedvoxel.png b/docs/src/images/architecture/drawcell/adaptedvoxel.png new file mode 100644 index 00000000..4d8c28b4 Binary files /dev/null and b/docs/src/images/architecture/drawcell/adaptedvoxel.png differ diff --git a/docs/src/images/architecture/drawcell/bisectedvoxel.png b/docs/src/images/architecture/drawcell/bisectedvoxel.png new file mode 100644 index 00000000..9da8d42f Binary files /dev/null and b/docs/src/images/architecture/drawcell/bisectedvoxel.png differ diff --git a/docs/src/images/architecture/drawcell/completevoxel.png b/docs/src/images/architecture/drawcell/completevoxel.png new file mode 100644 index 00000000..0dfc8897 Binary files /dev/null and b/docs/src/images/architecture/drawcell/completevoxel.png differ diff --git a/docs/src/progress/bigthings.md b/docs/src/progress/bigthings.md index 3680cf07..bd559a1a 100644 --- a/docs/src/progress/bigthings.md +++ b/docs/src/progress/bigthings.md @@ -1,6 +1,11 @@ @page bigthings Big Things I want to build # and may or may not have the sanity to build +DONE: + + +TODO(?): + - CFD - Internal Boundaries - Multigrid optimization @@ -8,6 +13,8 @@ - Transvoxel Algorithm + - Building cube voxels w/ LOD + - Deferred Shading Pipeline - Audio Ray Tracing @@ -17,3 +24,7 @@ - Massive scale creature groups (ie 10k armies) + - Use HTML to define ui windows + - Some css support + - Language file for translation + diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 9e8b4d2b..f758955e 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -371,9 +371,23 @@ Highlevel netcode gen updates - Furthermore, keep tracking of the existing ids for trees and fields and only generate ids for new trees and fields Fix client gravity tree name +(06/19/2024) +Transvoxel implementation + - Begin work on transvoxel algo + +(06/21/2024) +Transvoxel implementation + - First working implementation of mesh generation for transvoxel chunks (architecture of adding it to drawcellmanager still todo) + +(06/22/2024) +Transvoxel implementation + - Scaling LODed chunks by lod level + # TODO + + Demo requirements: = Assets = Block animation in first person @@ -400,6 +414,26 @@ Ability for private realms to have time start/stop based on the player's feedbac + + + + +Goof off today requirements: +Transvoxel implementation + - Fix draw cell manager requesting far-out chunks + - Properly update to higher LOD meshes as you get closer + Client Terrain Entity Management (specifically creation/teardown for client) + - Also queries for far out chunks to load far away terrain + Server Terrain Management (specifically for collision) + - Handles communicating far out LOD chunks to client as well + Terrain Interface Positional Access Interface + - Ability to get terrain at point for interactions with game world eg placing grass/water collision + + + + + + BIG BIG BIG BIG IMMEDIATE TO DO: always enforce opengl interface across all opengl calls jesus christ the bone uniform bug was impossible @@ -456,22 +490,6 @@ Shader library system - Abiltiy to include the shader library in individual files (ie implement #include) Break control handlers into separate files with new logic to transition between control handler states - - -Transvoxel Algorithm - Client Terrain Entity Management (specifically creation/teardown for client) - - Also queries for far out chunks to load far away terrain - Server Terrain Management (specifically for collision) - - Handles communicating far out LOD chunks to client as well - Terrain Interface Positional Access Interface - - Ability to get terrain at point for interactions with game world eg placing grass/water collision - Actually implement transvoxel algo - Marching Cubes Texture Overhaul - - Detect opengl max image size - - Construct texture atlas of max size - - (target 256x256 resolution initially, should give ~1000 types for 8192x8192) - - Prebake all textures into atlas - - Rewrite marching cubes shader to leverage this atlas Another pass at grass - Multiple foliage models in same cell @@ -580,9 +598,6 @@ Generic collision engine to support different instances of engine (eg hitboxes v - Major refactoring to happen here Procedural Cliff Texture - Uses noise or fractals or something to generate infinite textures in shader -Terrain Chunks: - - Scale textures to be 1 texture per unit of terrain - - Texture atlasing (512x512) Loot Generator - System that can generate items that would be appropriate reward given some variables - ie you tell it 'this is this character's stats, this is the relative level of loot I want to provide' diff --git a/docs/src/testing/testing.md b/docs/src/testing/testing.md new file mode 100644 index 00000000..f313197e --- /dev/null +++ b/docs/src/testing/testing.md @@ -0,0 +1,17 @@ +@page testing Testing + +Eventual goal is to have unit tests for parts of the engine that it makes sense for. Some ideas: + - Loading assets + - UI functionality + - Basic behavior trees + - Script Engine + - AI + - Networking + + +Current CI is Jenkins, which has a plugin +https://plugins.jenkins.io/xvfb/ +that allows for graphical sessions while building + +I need to figure out hooking this up to my container in order to do most of the above testing + diff --git a/net/terrain.json b/net/terrain.json index 25a05fff..bd70d2c8 100644 --- a/net/terrain.json +++ b/net/terrain.json @@ -90,6 +90,11 @@ "type" : "BYTE_ARRAY" }, + { + "name" : "chunkResolution", + "type" : "FIXED_INT" + }, + { "name" : "terrainWeight", "type" : "FIXED_FLOAT" @@ -186,6 +191,27 @@ "chunkData" ] }, + { + "messageName" : "RequestReducedChunkData", + "description" : "Requests reduced resolution chunk data from the server", + "data" : [ + "worldX", + "worldY", + "worldZ", + "chunkResolution" + ] + }, + { + "messageName" : "SendReducedChunkData", + "description" : "Sends chunk data to the client", + "data" : [ + "worldX", + "worldY", + "worldZ", + "chunkResolution", + "chunkData" + ] + }, { "messageName" : "RequestFluidData", "description" : "Requests a fluid data from the server", diff --git a/src/main/java/electrosphere/client/terrain/cells/DrawCell.java b/src/main/java/electrosphere/client/terrain/cells/DrawCell.java index 7e9ca219..2bd9a1a6 100644 --- a/src/main/java/electrosphere/client/terrain/cells/DrawCell.java +++ b/src/main/java/electrosphere/client/terrain/cells/DrawCell.java @@ -11,39 +11,47 @@ import electrosphere.entity.ClientEntityUtils; import electrosphere.entity.Entity; import electrosphere.entity.EntityUtils; import electrosphere.entity.types.terrain.TerrainChunk; -import electrosphere.renderer.shader.ShaderProgram; -import electrosphere.renderer.texture.Texture; -import electrosphere.server.datacell.Realm; +import electrosphere.renderer.meshgen.TransvoxelModelGeneration.TransvoxelChunkData; +import electrosphere.server.terrain.manager.ServerTerrainChunk; /** - * - * @author satellite + * A single drawcell - contains an entity that has a physics mesh and potentially graphics */ public class DrawCell { + + /** + * Enum for the different faces of a draw cell -- used when filling in data for higher LOD faces + */ + public enum DrawCellFace { + X_POSITIVE, + X_NEGATIVE, + Y_POSITIVE, + Y_NEGATIVE, + Z_POSITIVE, + Z_NEGATIVE, + } + //the position of the draw cell in world coordinates Vector3i worldPos; + //the main entity for the cell Entity modelEntity; + //the physics mesh DBody physicsObject; //Allocated once instead of continuously, used to generate the visual/physics models float[][][] weights = new float[ChunkData.CHUNK_DATA_GENERATOR_SIZE][ChunkData.CHUNK_DATA_GENERATOR_SIZE][ChunkData.CHUNK_DATA_GENERATOR_SIZE]; int[][][] types = new int[ChunkData.CHUNK_DATA_GENERATOR_SIZE][ChunkData.CHUNK_DATA_GENERATOR_SIZE][ChunkData.CHUNK_DATA_GENERATOR_SIZE]; + + + //the maximum detail LOD level + public static final int FULL_DETAIL_LOD = 0; + - static Texture groundTextureOne = new Texture("/Textures/Ground/Dirt1.png"); - static Texture groundTextureTwo = new Texture("/Textures/Ground/Dirt1.png"); - static Texture groundTextureThree = new Texture("/Textures/Ground/Dirt1.png"); - static Texture groundTextureFour = new Texture("/Textures/Ground/Dirt1.png"); - - static { -// groundTextureOne = new Texture("/Textures/Ground/GrassTileable.png"); -// groundTextureTwo = new Texture("/Textures/Ground/Dirt1.png"); -// groundTextureThree = new Texture("/Textures/Ground/Dirt1.png"); -// groundTextureFour = new Texture("/Textures/Ground/Dirt1.png"); - } - - + /** + * Private constructor + */ DrawCell(){ } @@ -63,18 +71,23 @@ public class DrawCell { /** * Generates a drawable entity based on this chunk */ - public void generateDrawableEntity(VoxelTextureAtlas atlas){ + public void generateDrawableEntity(VoxelTextureAtlas atlas, int lod, DrawCellFace higherLODFace){ if(modelEntity != null){ Globals.clientScene.deregisterEntity(modelEntity); } + this.fillInData(); + TransvoxelChunkData chunkData = new TransvoxelChunkData(weights, types, lod); + if(lod > FULL_DETAIL_LOD){ - fillInData(); - - modelEntity = TerrainChunk.clientCreateTerrainChunkEntity(weights, types, 0, atlas); - + } + modelEntity = TerrainChunk.clientCreateTerrainChunkEntity(chunkData, lod, atlas); ClientEntityUtils.initiallyPositionEntity(modelEntity, getRealPos()); } + /** + * Gets the real-space position of the draw cell + * @return the real-space position + */ protected Vector3d getRealPos(){ return new Vector3d( worldPos.x * ChunkData.CHUNK_SIZE, @@ -224,5 +237,49 @@ public class DrawCell { types[ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE] = 0; } } + + + /** + * Fills in the internal arrays of data for generate terrain models + */ + private void fillInData(TransvoxelChunkData chunkData, int lod, DrawCellFace face){ + float[][] faceData = new float[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE]; + int[][] atlasData = new int[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE]; + switch(face){ + case X_POSITIVE: { + } break; + case X_NEGATIVE: { + } break; + case Y_POSITIVE: { + } break; + case Y_NEGATIVE: { + } break; + case Z_POSITIVE: { + } break; + case Z_NEGATIVE: { + } break; + } + // + //fill in data + // + //main chunk + //face X + // if(worldPos.x + 1 < Globals.clientWorldData.getWorldDiscreteSize()){ + // currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos.x + 1, worldPos.y, worldPos.z); + // for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){ + // for(int j = 0; j < ChunkData.CHUNK_SIZE; j++){ + // weights[ChunkData.CHUNK_SIZE][i][j] = currentChunk.getWeight(0, i, j); + // types[ChunkData.CHUNK_SIZE][i][j] = currentChunk.getType(0, i, j); + // } + // } + // } else { + // for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){ + // for(int j = 0; j < ChunkData.CHUNK_SIZE; j++){ + // weights[ChunkData.CHUNK_SIZE][i][j] = -1; + // types[ChunkData.CHUNK_SIZE][i][j] = 0; + // } + // } + // } + } } diff --git a/src/main/java/electrosphere/client/terrain/cells/DrawCellManager.java b/src/main/java/electrosphere/client/terrain/cells/DrawCellManager.java index b5e332d9..4f7a3508 100644 --- a/src/main/java/electrosphere/client/terrain/cells/DrawCellManager.java +++ b/src/main/java/electrosphere/client/terrain/cells/DrawCellManager.java @@ -9,15 +9,40 @@ import org.joml.Vector3d; import org.joml.Vector3i; import electrosphere.client.terrain.cache.ChunkData; +import electrosphere.client.terrain.cells.DrawCell.DrawCellFace; import electrosphere.client.terrain.manager.ClientTerrainManager; import electrosphere.engine.Globals; import electrosphere.entity.EntityUtils; import electrosphere.net.parser.net.message.TerrainMessage; import electrosphere.renderer.shader.ShaderProgram; +import electrosphere.server.terrain.manager.ServerTerrainChunk; /** - * - * @author satellite + * Manages the graphical entities for the terrain chunks + * + * + * +Notes for integrating with transvoxel algo: +Different problems to tackle + +For all chunks within minimum radius, check if they can be updated and update accordingly <--- we do this currently + + +For all chunks between minimum radius and first LOD radius, check if we can make a LOD chunk +If we can, make a lod chunk or see if it can be updated +This check will be: +For every position, check if all four positions required for LOD chunk are within lod radius + if yes, make lod chunk + +If we cannot, create a fullres chunk +This check will be: +For every position, check if all four positions required for LOD chunk are within lod radius + if yes, make lod chunk + if they are outside the far bound, create lod chunk + if they are within the near bound, create a fullres chunk + + + */ public class DrawCellManager { @@ -27,45 +52,46 @@ public class DrawCellManager { int cellY; int cellZ; - - //the dimensions of the world that this cell manager can handles - int cellWidth; - - //the width of a minicell in this manager - int miniCellWidth; - //all currently displaying mini cells - Set cells; + Set cells = new HashSet(); Map keyCellMap = new HashMap(); - Set hasNotRequested; - Set hasRequested; - Set drawable; - Set undrawable; - Set updateable; + + //status of all position keys + Set hasNotRequested = new HashSet(); + Set requested = new HashSet(); + Set drawable = new HashSet(); + Set undrawable = new HashSet(); + Set updateable = new HashSet(); + + //LOD level of all position keys + Map positionLODLevel = new HashMap(); //voxel atlas VoxelTextureAtlas atlas; - + //shader program for drawable cells ShaderProgram program; - // int drawRadius = 5; - int drawStepdownInterval = 3; - int drawStepdownValue = 25; + //the real-space radius for which we will construct draw cells inside of + //ie, we check if the draw cell's entity would be inside this radius. If it would, create the draw cell, otherwise don't + double drawFullModelRadius = 50; - double drawRadius = 50; + //the radius we'll draw LODed chunks for + double drawLODRadius = drawFullModelRadius + ServerTerrainChunk.CHUNK_DIMENSION * (2*2 + 4*4 + 8*8 + 16*16); + + + //the number of possible LOD levels + //1,2,4,8,16 + static final int NUMBER_OF_LOD_LEVELS = 5; + + //the table of lod leve -> radius at which we will look for chunks within this log + double[] lodLevelRadiusTable = new double[5]; + //the radius for which physics meshes are created when draw cells are created int physicsRadius = 3; - int worldBoundDiscreteMin = 0; - int worldBoundDiscreteMax = 0; - - //client terrain manager - // ClientTerrainManager clientTerrainManager; - - //ready to start updating? boolean update = false; @@ -82,22 +108,22 @@ public class DrawCellManager { * @param discreteY The initial discrete position Y coordinate */ public DrawCellManager(ClientTerrainManager clientTerrainManager, int discreteX, int discreteY, int discreteZ){ - worldBoundDiscreteMax = (int)(Globals.clientWorldData.getWorldBoundMin().x / Globals.clientWorldData.getDynamicInterpolationRatio() * 1.0f); - cells = new HashSet(); - hasNotRequested = new HashSet(); - drawable = new HashSet(); - undrawable = new HashSet(); - updateable = new HashSet(); - hasRequested = new HashSet(); - cellX = discreteX; cellY = discreteY; cellZ = discreteZ; program = Globals.terrainShaderProgram; + + //the first lod level is set by user + lodLevelRadiusTable[0] = drawFullModelRadius; + //generate LOD radius table + for(int i = 1; i < NUMBER_OF_LOD_LEVELS; i++){ + double sizeOfSingleModel = Math.pow(2,i) * ServerTerrainChunk.CHUNK_DIMENSION; + //size of the radius for this lod level should be three times the size of a model + the previous radius + //this guarantees we get at least one adapter chunk, one proper chunk, and also that the radius accounts for the previous lod level chunks + lodLevelRadiusTable[i] = lodLevelRadiusTable[i-1] + sizeOfSingleModel * 3; + } - // drawRadius = Globals.userSettings.getGraphicsPerformanceLODChunkRadius(); - drawStepdownInterval = Globals.userSettings.getGameplayPhysicsCellRadius(); physicsRadius = Globals.userSettings.getGameplayPhysicsCellRadius(); invalidateAllCells(); @@ -105,29 +131,41 @@ public class DrawCellManager { update = true; } + /** + * Private constructor + */ DrawCellManager(){ } - public void setCell(Vector3i cellPos){ + /** + * Sets the player's current position in cell-space + * @param cellPos The cell's position + */ + public void setPlayerCell(Vector3i cellPos){ cellX = cellPos.x; cellY = cellPos.y; cellZ = cellPos.z; } + /** + * Update function that is called if a cell has not been requested + */ void updateUnrequestedCell(){ if(hasNotRequested.size() > 0){ String targetKey = hasNotRequested.iterator().next(); hasNotRequested.remove(targetKey); Vector3i worldPos = getVectorFromKey(targetKey); - // Vector3i vector = getVectorFromKey(targetKey); - // int currentCellX = cellX - drawRadius + vector.x; - // int currentCellY = cellY - drawRadius + vector.y; - // int currentCellZ = cellZ - drawRadius + vector.z; + + // + //Because of the way marching cubes works, we need to request the adjacent chunks so we know how to properly blend between one chunk and the next + //The following loop-hell does this + // for(int i = 0; i < 2; i++){ for(int j = 0; j < 2; j++){ for(int k = 0; k < 2; k++){ Vector3i posToCheck = new Vector3i(worldPos).add(i,j,k); + String requestKey = getCellKey(posToCheck.x,posToCheck.y,posToCheck.z); if( posToCheck.x >= 0 && posToCheck.x < Globals.clientWorldData.getWorldDiscreteSize() && @@ -137,20 +175,20 @@ public class DrawCellManager { posToCheck.z < Globals.clientWorldData.getWorldDiscreteSize() && !Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z) ){ - // if(!hasRequested.contains(targetKey)){ + if(!requested.contains(requestKey)){ //client should request chunk data from server for each chunk necessary to create the model Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestChunkDataMessage( posToCheck.x, posToCheck.y, posToCheck.z )); - // } + } } - undrawable.add(targetKey); - hasRequested.add(targetKey); } } } + undrawable.add(targetKey); + requested.add(targetKey); } } @@ -158,10 +196,12 @@ public class DrawCellManager { * Makes one of the undrawable cells drawable */ void makeCellDrawable(){ - if(undrawable.size() > 0){ String targetKey = undrawable.iterator().next(); Vector3i worldPos = getVectorFromKey(targetKey); + + // + //Checks if all chunk data necessary to generate a mesh is present boolean containsNecessaryChunks = true; for(int i = 0; i < 2; i++){ for(int j = 0; j < 2; j++){ @@ -175,25 +215,28 @@ public class DrawCellManager { posToCheck.z < Globals.clientWorldData.getWorldDiscreteSize() && !containsChunkDataAtWorldPoint(posToCheck.x,posToCheck.y,posToCheck.z) ){ - containsChunkDataAtWorldPoint(posToCheck.x,posToCheck.y,posToCheck.z); containsNecessaryChunks = false; } } } } - //if contains all chunks necessary to generate visuals + // + //if contains data for all chunks necessary to generate visuals if(containsNecessaryChunks){ - //build float array + + //update the status of the terrain key + undrawable.remove(targetKey); + drawable.add(targetKey); + + //build the cell DrawCell cell = DrawCell.generateTerrainCell( worldPos ); cells.add(cell); keyCellMap.put(targetKey,cell); - // undrawable.add(targetKey); - undrawable.remove(targetKey); - drawable.add(targetKey); - //make drawable entity - keyCellMap.get(targetKey).generateDrawableEntity(atlas); + DrawCellFace higherLODFace = null; + keyCellMap.get(targetKey).generateDrawableEntity(atlas,0,higherLODFace); + //evaluate for foliage Globals.clientFoliageManager.evaluateChunk(worldPos); } @@ -216,37 +259,49 @@ public class DrawCellManager { worldPos.z >= 0 && worldPos.z < Globals.clientWorldData.getWorldDiscreteSize() ){ -// if(Math.abs(drawRadius + 1 - targetX) < physicsRadius && Math.abs(drawRadius + 1 - targetY) < physicsRadius){ -// needsPhysics[targetX][targetY] = true; -// } - // int dist = (int)Math.sqrt((targetX - drawRadius)*(targetX - drawRadius) + (targetY - drawRadius) * (targetY - drawRadius)); //Math.abs(targetX - drawRadius) * Math.abs(targetY - drawRadius); - // int stride = Math.min(commonWorldData.getDynamicInterpolationRatio()/2, Math.max(1, dist / drawStepdownInterval * drawStepdownValue)); - // while(commonWorldData.getDynamicInterpolationRatio() % stride != 0){ - // stride = stride + 1; - // } keyCellMap.get(targetKey).destroy(); - keyCellMap.get(targetKey).generateDrawableEntity(atlas); + DrawCellFace higherLODFace = null; + keyCellMap.get(targetKey).generateDrawableEntity(atlas,0,higherLODFace); } drawable.add(targetKey); } } + + + + /** + * Checks if the manager contains a cell position that hasn't had its chunk data requested from the server yet + * @return true if there is an unrequested cell, false otherwise + */ public boolean containsUnrequestedCell(){ return hasNotRequested.size() > 0; } + /** + * Checks if the manager contains a cell who hasn't been made drawable yet + * @return true if there is an undrawable cell, false otherwise + */ public boolean containsUndrawableCell(){ return undrawable.size() > 0; } + /** + * Checks if the manager contains a cell who needs to be updated + * @return true if there is an updateable cell, false otherwise + */ public boolean containsUpdateableCell(){ return updateable.size() > 0; } - + /** + * Transforms a real coordinate into a cell-space coordinate + * @param input the real coordinate + * @return the cell coordinate + */ public int transformRealSpaceToCellSpace(double input){ return (int)(input / Globals.clientWorldData.getDynamicInterpolationRatio()); } @@ -278,7 +333,7 @@ public class DrawCellManager { Set cellsToRemove = new HashSet(); for(DrawCell cell : cells){ Vector3d realPos = cell.getRealPos(); - if(Globals.playerEntity != null && EntityUtils.getPosition(Globals.playerEntity).distance(realPos) > drawRadius){ + if(Globals.playerEntity != null && EntityUtils.getPosition(Globals.playerEntity).distance(realPos) > drawFullModelRadius){ cellsToRemove.add(cell); } } @@ -290,7 +345,7 @@ public class DrawCellManager { undrawable.remove(key); updateable.remove(key); keyCellMap.remove(key); - hasRequested.remove(key); + requested.remove(key); cell.destroy(); } } @@ -301,9 +356,9 @@ public class DrawCellManager { private void queueNewCells(){ if(Globals.playerEntity != null && Globals.clientWorldData != null){ Vector3d playerPos = EntityUtils.getPosition(Globals.playerEntity); - for(int x = -(int)drawRadius; x < drawRadius; x = x + ChunkData.CHUNK_SIZE){ - for(int y = -(int)drawRadius; y < drawRadius; y = y + ChunkData.CHUNK_SIZE){ - for(int z = -(int)drawRadius; z < drawRadius; z = z + ChunkData.CHUNK_SIZE){ + for(int x = -(int)drawFullModelRadius; x < drawFullModelRadius; x = x + ChunkData.CHUNK_SIZE){ + for(int y = -(int)drawFullModelRadius; y < drawFullModelRadius; y = y + ChunkData.CHUNK_SIZE){ + for(int z = -(int)drawFullModelRadius; z < drawFullModelRadius; z = z + ChunkData.CHUNK_SIZE){ Vector3d newPos = new Vector3d(playerPos.x + x, playerPos.y + y, playerPos.z + z); Vector3i worldPos = new Vector3i( Globals.clientWorldData.convertRealToChunkSpace(newPos.x), @@ -316,7 +371,7 @@ public class DrawCellManager { Globals.clientWorldData.convertChunkToRealSpace(worldPos.z) ); if( - playerPos.distance(chunkRealSpace) < drawRadius && + playerPos.distance(chunkRealSpace) < drawFullModelRadius && worldPos.x >= 0 && worldPos.x < Globals.clientWorldData.getWorldDiscreteSize() && worldPos.y >= 0 && @@ -330,7 +385,7 @@ public class DrawCellManager { Globals.clientWorldData.convertRealToChunkSpace(chunkRealSpace.z) ); if(!keyCellMap.containsKey(key) && !hasNotRequested.contains(key) && !undrawable.contains(key) && !drawable.contains(key) && - !hasRequested.contains(key)){ + !requested.contains(key)){ hasNotRequested.add(key); } } @@ -355,27 +410,20 @@ public class DrawCellManager { } /** - * Splits a cell key into its constituent coordinates in array format. - * @param cellKey The cell key to split - * @return The coordinates in array format + * Controls whether the client generates drawable chunks or just physics chunks (ie if running a headless client) + * @param generate true to generate graphics, false otherwise */ - // private int[] splitKeyToCoordinates(String cellKey){ - // int[] rVal = new int[3]; - // String[] components = cellKey.split("_"); - // for(int i = 0; i < 3; i++){ - // rVal[i] = Integer.parseInt(components[i]); - // } - // return rVal; - // } - - public boolean coordsInPhysicsSpace(int worldX, int worldY){ - return worldX <= cellX + physicsRadius && worldX >= cellX - physicsRadius && worldY <= cellY + physicsRadius && worldY >= cellY - physicsRadius; - } - public void setGenerateDrawables(boolean generate){ this.generateDrawables = generate; } + /** + * Checks if the terrain cache has a chunk at a given world point + * @param worldX the x coordinate + * @param worldY the y coordinate + * @param worldZ the z coordinate + * @return true if the chunk data exists, false otherwise + */ boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){ if(Globals.clientTerrainManager != null){ return Globals.clientTerrainManager.containsChunkDataAtWorldPoint(worldX,worldY,worldZ); @@ -428,11 +476,11 @@ public class DrawCellManager { /** - * Gets the draw radius - * @return the draw radius + * Gets the radius within which full-detail models are drawn + * @return the radius */ - public double getDrawRadius(){ - return drawRadius; + public double getDrawFullModelRadius(){ + return drawFullModelRadius; } /** @@ -444,6 +492,5 @@ public class DrawCellManager { -// public } diff --git a/src/main/java/electrosphere/client/terrain/cells/VoxelTextureAtlas.java b/src/main/java/electrosphere/client/terrain/cells/VoxelTextureAtlas.java index 490d31ce..fe2f0e42 100644 --- a/src/main/java/electrosphere/client/terrain/cells/VoxelTextureAtlas.java +++ b/src/main/java/electrosphere/client/terrain/cells/VoxelTextureAtlas.java @@ -74,7 +74,7 @@ public class VoxelTextureAtlas { * @return the index in the atlas of the texture of the provided voxel type */ public int getVoxelTypeOffset(int voxelTypeId){ - return typeCoordMap.get(voxelTypeId); + return typeCoordMap.containsKey(voxelTypeId) ? typeCoordMap.get(voxelTypeId) : -1; } } diff --git a/src/main/java/electrosphere/client/terrain/editing/TerrainEditing.java b/src/main/java/electrosphere/client/terrain/editing/TerrainEditing.java index ccd375ef..06b7fce1 100644 --- a/src/main/java/electrosphere/client/terrain/editing/TerrainEditing.java +++ b/src/main/java/electrosphere/client/terrain/editing/TerrainEditing.java @@ -1,9 +1,7 @@ package electrosphere.client.terrain.editing; import org.joml.Vector3d; -import org.joml.Vector3i; -import electrosphere.client.terrain.cache.ChunkData; import electrosphere.engine.Globals; import electrosphere.net.parser.net.message.TerrainMessage; diff --git a/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java b/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java index d6194196..9b30da9b 100644 --- a/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java +++ b/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java @@ -6,7 +6,6 @@ import java.nio.IntBuffer; import java.util.Collection; import java.util.LinkedList; import java.util.List; -import java.util.Set; import java.util.UUID; import java.util.concurrent.CopyOnWriteArrayList; @@ -36,7 +35,7 @@ public class ClientTerrainManager { public static final int INTERPOLATION_RATIO = ServerTerrainManager.SERVER_TERRAIN_MANAGER_INTERPOLATION_RATIO; //caches chunks from server - static final int CACHE_SIZE = 50; + static final int CACHE_SIZE = 500; //used for caching the macro values ClientTerrainCache terrainCache; @@ -55,6 +54,9 @@ public class ClientTerrainManager { } + /** + * Handles messages that have been received from the server + */ public void handleMessages(){ List bouncedMessages = new LinkedList(); for(TerrainMessage message : messageQueue){ @@ -99,14 +101,32 @@ public class ClientTerrainManager { } } + /** + * Attaches a terrain message to the queue of messages that this manager needs to process + * @param message The message + */ public void attachTerrainMessage(TerrainMessage message){ messageQueue.add(message); } + /** + * Checks if the terrain cache contains chunk data at a given world position + * @param worldX the x position + * @param worldY the y position + * @param worldZ the z position + * @return true if the data exists, false otherwise + */ public boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){ return terrainCache.containsChunkDataAtWorldPoint(worldX, worldY, worldZ); } + /** + * Checks that the cache contains chunk data at a real-space coordinate + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @return true if the cache contains the chunk data at the coordinate, false otherwise + */ public boolean containsChunkDataAtRealPoint(double x, double y, double z){ assert clientWorldData != null; return terrainCache.containsChunkDataAtWorldPoint( diff --git a/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java b/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java index 90a5e094..95563480 100644 --- a/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java +++ b/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java @@ -6,6 +6,7 @@ import java.util.concurrent.TimeUnit; import org.joml.Quaterniond; import org.joml.Vector3d; import org.joml.Vector3f; +import org.joml.Vector3i; import electrosphere.audio.AudioUtils; import electrosphere.audio.VirtualAudioSource; @@ -15,7 +16,9 @@ import electrosphere.client.fluid.cells.FluidCellManager; import electrosphere.client.foliagemanager.ClientFoliageManager; import electrosphere.client.sim.ClientSimulation; import electrosphere.client.targeting.crosshair.Crosshair; +import electrosphere.client.terrain.cells.DrawCell; import electrosphere.client.terrain.cells.DrawCellManager; +import electrosphere.client.terrain.cells.DrawCell.DrawCellFace; import electrosphere.collision.CollisionEngine; import electrosphere.controls.ControlHandler; import electrosphere.engine.Globals; @@ -28,6 +31,7 @@ import electrosphere.entity.EntityUtils; import electrosphere.entity.btree.BehaviorTree; import electrosphere.entity.state.movement.ApplyRotationTree; import electrosphere.entity.types.camera.CameraEntityUtils; +import electrosphere.entity.types.terrain.TerrainChunk; import electrosphere.entity.types.tree.ProceduralTree; import electrosphere.logger.LoggerInterface; import electrosphere.menu.MenuGenerators; @@ -36,8 +40,10 @@ import electrosphere.menu.WindowUtils; import electrosphere.menu.mainmenu.MenuGeneratorsMultiplayer; import electrosphere.net.NetUtils; import electrosphere.net.client.ClientNetworking; +import electrosphere.renderer.meshgen.TransvoxelModelGeneration.TransvoxelChunkData; import electrosphere.renderer.ui.elements.Window; import electrosphere.server.datacell.EntityDataCellMapper; +import electrosphere.server.terrain.manager.ServerTerrainChunk; import electrosphere.util.MathUtils; public class ClientLoading { @@ -180,7 +186,43 @@ public class ClientLoading { } else { Globals.playerCamera = CameraEntityUtils.spawnPlayerEntityTrackingCameraFirstPersonEntity(new Vector3f(1,0,1), MathUtils.getOriginVectorf()); } - + + + /** + * Test LODed chunk + */ + float[][][] isoValues = new float[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE]; + int[][][] atlasValues = new int[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE]; + float[][] edgeIsoValues = new float[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE + ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE + ServerTerrainChunk.CHUNK_DIMENSION]; + int[][] edgeAtlasValues = new int[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE + ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE + ServerTerrainChunk.CHUNK_DIMENSION]; + for(int x = 0; x < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; x++){ + for(int y = 0; y < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; y++){ + for(int z = 0; z < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; z++){ + if(x < 5 && y < 5){ + isoValues[x][y][z] = 1.0f; + } + atlasValues[x][y][z] = 1; + } + if(y < 10 && x < 10){ + edgeIsoValues[x][y] = 1.0f; + } + if(y == 3 && x == 3){ + edgeIsoValues[x][y] = -1.0f; + } + edgeAtlasValues[x][y] = 1; + } + } + TransvoxelChunkData chunkData = new TransvoxelChunkData( + isoValues, + atlasValues, + 1 + ); + chunkData.addZNegativeEdge( + edgeIsoValues, + edgeAtlasValues + ); + Entity transvoxelEntityTest = TerrainChunk.clientCreateTerrainChunkEntity(chunkData, 1, Globals.voxelTextureAtlas); + ClientEntityUtils.initiallyPositionEntity(transvoxelEntityTest, new Vector3d(3,3,3)); /* diff --git a/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java b/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java index e139481c..36941edb 100644 --- a/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java +++ b/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java @@ -2,6 +2,7 @@ package electrosphere.entity.types.terrain; import org.joml.Vector3d; +import electrosphere.client.terrain.cells.DrawCell; import electrosphere.client.terrain.cells.VoxelTextureAtlas; import electrosphere.client.terrain.manager.ClientTerrainManager; import electrosphere.collision.PhysicsEntityUtils; @@ -11,6 +12,8 @@ import electrosphere.entity.EntityCreationUtils; import electrosphere.entity.EntityDataStrings; import electrosphere.entity.ServerEntityUtils; import electrosphere.renderer.meshgen.TerrainChunkModelGeneration; +import electrosphere.renderer.meshgen.TransvoxelModelGeneration; +import electrosphere.renderer.meshgen.TransvoxelModelGeneration.TransvoxelChunkData; import electrosphere.server.datacell.Realm; /** @@ -21,19 +24,23 @@ public class TerrainChunk { /** * Creates a client terrain chunk based on weights and values provided - * @param weights The terrain weights - * @param values The values (block types) + * @param chunkData the chunk data to generate with * @param levelOfDetail Increasing value that increments level of detail. 0 would be full resolution, 1 would be half resolution and so on. Only generates physics if levelOfDetail is 0 * @return The terrain chunk entity */ - public static Entity clientCreateTerrainChunkEntity(float[][][] weights, int[][][] values, int levelOfDetail, VoxelTextureAtlas atlas){ + public static Entity clientCreateTerrainChunkEntity(TransvoxelChunkData chunkData, int levelOfDetail, VoxelTextureAtlas atlas){ - TerrainChunkData data = TerrainChunkModelGeneration.generateTerrainChunkData(weights, values); + TerrainChunkData data; + if(levelOfDetail == DrawCell.FULL_DETAIL_LOD){ + data = TerrainChunkModelGeneration.generateTerrainChunkData(chunkData.terrainGrid, chunkData.textureGrid, levelOfDetail); + } else { + data = TransvoxelModelGeneration.generateTerrainChunkData(chunkData); + } String modelPath = ClientTerrainManager.queueTerrainGridGeneration(data, atlas); Entity rVal = EntityCreationUtils.createClientSpatialEntity(); EntityCreationUtils.makeEntityDrawablePreexistingModel(rVal, modelPath); - if(data.vertices.size() > 0 && levelOfDetail < 1){ + if(data.vertices.size() > 0 && levelOfDetail == DrawCell.FULL_DETAIL_LOD){ PhysicsEntityUtils.clientAttachTerrainChunkRigidBody(rVal, data); } @@ -53,7 +60,7 @@ public class TerrainChunk { */ public static Entity serverCreateTerrainChunkEntity(Realm realm, Vector3d position, float[][][] weights, int[][][] values){ - TerrainChunkData data = TerrainChunkModelGeneration.generateTerrainChunkData(weights, values); + TerrainChunkData data = TerrainChunkModelGeneration.generateTerrainChunkData(weights, values, DrawCell.FULL_DETAIL_LOD); Entity rVal = EntityCreationUtils.createServerEntity(realm, position); if(data.vertices.size() > 0){ diff --git a/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java b/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java index 49e4cfb6..86795b3f 100644 --- a/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java +++ b/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java @@ -176,6 +176,16 @@ SYNCHRONIZATION_MESSAGE, rVal = TerrainMessage.parsesendChunkDataMessage(byteBuffer); } break; + case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTREDUCEDCHUNKDATA: + if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){ + rVal = TerrainMessage.parseRequestReducedChunkDataMessage(byteBuffer); + } + break; + case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDREDUCEDCHUNKDATA: + if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){ + rVal = TerrainMessage.parseSendReducedChunkDataMessage(byteBuffer); + } + break; case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA: if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){ rVal = TerrainMessage.parseRequestFluidDataMessage(byteBuffer); diff --git a/src/main/java/electrosphere/net/parser/net/message/TerrainMessage.java b/src/main/java/electrosphere/net/parser/net/message/TerrainMessage.java index bc47ab9c..adcf912c 100644 --- a/src/main/java/electrosphere/net/parser/net/message/TerrainMessage.java +++ b/src/main/java/electrosphere/net/parser/net/message/TerrainMessage.java @@ -16,6 +16,8 @@ public class TerrainMessage extends NetworkMessage { SPAWNPOSITION, REQUESTCHUNKDATA, SENDCHUNKDATA, + REQUESTREDUCEDCHUNKDATA, + SENDREDUCEDCHUNKDATA, REQUESTFLUIDDATA, SENDFLUIDDATA, UPDATEFLUIDDATA, @@ -40,6 +42,7 @@ public class TerrainMessage extends NetworkMessage { double realLocationY; double realLocationZ; byte[] chunkData; + int chunkResolution; float terrainWeight; int terrainValue; @@ -196,6 +199,14 @@ public class TerrainMessage extends NetworkMessage { this.chunkData = chunkData; } + public int getchunkResolution() { + return chunkResolution; + } + + public void setchunkResolution(int chunkResolution) { + this.chunkResolution = chunkResolution; + } + public float getterrainWeight() { return terrainWeight; } @@ -262,6 +273,14 @@ public class TerrainMessage extends NetworkMessage { } case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDCHUNKDATA: return TerrainMessage.canParsesendChunkDataMessage(byteBuffer); + case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTREDUCEDCHUNKDATA: + if(byteBuffer.getRemaining() >= TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTREDUCEDCHUNKDATA_SIZE){ + return true; + } else { + return false; + } + case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDREDUCEDCHUNKDATA: + return TerrainMessage.canParseSendReducedChunkDataMessage(byteBuffer); case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA: if(byteBuffer.getRemaining() >= TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA_SIZE){ return true; @@ -478,6 +497,79 @@ public class TerrainMessage extends NetworkMessage { return rVal; } + public static TerrainMessage parseRequestReducedChunkDataMessage(CircularByteBuffer byteBuffer){ + TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTREDUCEDCHUNKDATA); + stripPacketHeader(byteBuffer); + rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); + rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); + rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); + rVal.setchunkResolution(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); + return rVal; + } + + public static TerrainMessage constructRequestReducedChunkDataMessage(int worldX,int worldY,int worldZ,int chunkResolution){ + TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTREDUCEDCHUNKDATA); + rVal.setworldX(worldX); + rVal.setworldY(worldY); + rVal.setworldZ(worldZ); + rVal.setchunkResolution(chunkResolution); + rVal.serialize(); + return rVal; + } + + public static boolean canParseSendReducedChunkDataMessage(CircularByteBuffer byteBuffer){ + int currentStreamLength = byteBuffer.getRemaining(); + List temporaryByteQueue = new LinkedList(); + if(currentStreamLength < 6){ + return false; + } + if(currentStreamLength < 10){ + return false; + } + if(currentStreamLength < 14){ + return false; + } + if(currentStreamLength < 18){ + return false; + } + int chunkDataSize = 0; + if(currentStreamLength < 22){ + return false; + } else { + temporaryByteQueue.add(byteBuffer.peek(18 + 0)); + temporaryByteQueue.add(byteBuffer.peek(18 + 1)); + temporaryByteQueue.add(byteBuffer.peek(18 + 2)); + temporaryByteQueue.add(byteBuffer.peek(18 + 3)); + chunkDataSize = ByteStreamUtils.popIntFromByteQueue(temporaryByteQueue); + } + if(currentStreamLength < 22 + chunkDataSize){ + return false; + } + return true; + } + + public static TerrainMessage parseSendReducedChunkDataMessage(CircularByteBuffer byteBuffer){ + TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDREDUCEDCHUNKDATA); + stripPacketHeader(byteBuffer); + rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); + rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); + rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); + rVal.setchunkResolution(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); + rVal.setchunkData(ByteStreamUtils.popByteArrayFromByteQueue(byteBuffer)); + return rVal; + } + + public static TerrainMessage constructSendReducedChunkDataMessage(int worldX,int worldY,int worldZ,int chunkResolution,byte[] chunkData){ + TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDREDUCEDCHUNKDATA); + rVal.setworldX(worldX); + rVal.setworldY(worldY); + rVal.setworldZ(worldZ); + rVal.setchunkResolution(chunkResolution); + rVal.setchunkData(chunkData); + rVal.serialize(); + return rVal; + } + public static TerrainMessage parseRequestFluidDataMessage(CircularByteBuffer byteBuffer){ TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTFLUIDDATA); stripPacketHeader(byteBuffer); @@ -807,6 +899,59 @@ public class TerrainMessage extends NetworkMessage { rawBytes[18+i] = chunkData[i]; } break; + case REQUESTREDUCEDCHUNKDATA: + rawBytes = new byte[2+4+4+4+4]; + //message header + rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN; + //entity messaage header + rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTREDUCEDCHUNKDATA; + intValues = ByteStreamUtils.serializeIntToBytes(worldX); + for(int i = 0; i < 4; i++){ + rawBytes[2+i] = intValues[i]; + } + intValues = ByteStreamUtils.serializeIntToBytes(worldY); + for(int i = 0; i < 4; i++){ + rawBytes[6+i] = intValues[i]; + } + intValues = ByteStreamUtils.serializeIntToBytes(worldZ); + for(int i = 0; i < 4; i++){ + rawBytes[10+i] = intValues[i]; + } + intValues = ByteStreamUtils.serializeIntToBytes(chunkResolution); + for(int i = 0; i < 4; i++){ + rawBytes[14+i] = intValues[i]; + } + break; + case SENDREDUCEDCHUNKDATA: + rawBytes = new byte[2+4+4+4+4+4+chunkData.length]; + //message header + rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN; + //entity messaage header + rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_SENDREDUCEDCHUNKDATA; + intValues = ByteStreamUtils.serializeIntToBytes(worldX); + for(int i = 0; i < 4; i++){ + rawBytes[2+i] = intValues[i]; + } + intValues = ByteStreamUtils.serializeIntToBytes(worldY); + for(int i = 0; i < 4; i++){ + rawBytes[6+i] = intValues[i]; + } + intValues = ByteStreamUtils.serializeIntToBytes(worldZ); + for(int i = 0; i < 4; i++){ + rawBytes[10+i] = intValues[i]; + } + intValues = ByteStreamUtils.serializeIntToBytes(chunkResolution); + for(int i = 0; i < 4; i++){ + rawBytes[14+i] = intValues[i]; + } + intValues = ByteStreamUtils.serializeIntToBytes(chunkData.length); + for(int i = 0; i < 4; i++){ + rawBytes[18+i] = intValues[i]; + } + for(int i = 0; i < chunkData.length; i++){ + rawBytes[22+i] = chunkData[i]; + } + break; case REQUESTFLUIDDATA: rawBytes = new byte[2+4+4+4]; //message header diff --git a/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java b/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java index c6be057e..5a808ed5 100644 --- a/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java +++ b/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java @@ -69,9 +69,11 @@ Message categories public static final byte TERRAIN_MESSAGE_TYPE_SPAWNPOSITION = 5; public static final byte TERRAIN_MESSAGE_TYPE_REQUESTCHUNKDATA = 6; public static final byte TERRAIN_MESSAGE_TYPE_SENDCHUNKDATA = 7; - public static final byte TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA = 8; - public static final byte TERRAIN_MESSAGE_TYPE_SENDFLUIDDATA = 9; - public static final byte TERRAIN_MESSAGE_TYPE_UPDATEFLUIDDATA = 10; + public static final byte TERRAIN_MESSAGE_TYPE_REQUESTREDUCEDCHUNKDATA = 8; + public static final byte TERRAIN_MESSAGE_TYPE_SENDREDUCEDCHUNKDATA = 9; + public static final byte TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA = 10; + public static final byte TERRAIN_MESSAGE_TYPE_SENDFLUIDDATA = 11; + public static final byte TERRAIN_MESSAGE_TYPE_UPDATEFLUIDDATA = 12; /* Terrain packet sizes */ @@ -82,6 +84,7 @@ Message categories public static final byte TERRAIN_MESSAGE_TYPE_REQUESTUSETERRAINPALETTE_SIZE = 38; public static final byte TERRAIN_MESSAGE_TYPE_SPAWNPOSITION_SIZE = 26; public static final byte TERRAIN_MESSAGE_TYPE_REQUESTCHUNKDATA_SIZE = 14; + public static final byte TERRAIN_MESSAGE_TYPE_REQUESTREDUCEDCHUNKDATA_SIZE = 18; public static final byte TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA_SIZE = 14; /* Server subcategories diff --git a/src/main/java/electrosphere/renderer/meshgen/TerrainChunkModelGeneration.java b/src/main/java/electrosphere/renderer/meshgen/TerrainChunkModelGeneration.java index 0ae198a1..27eae355 100644 --- a/src/main/java/electrosphere/renderer/meshgen/TerrainChunkModelGeneration.java +++ b/src/main/java/electrosphere/renderer/meshgen/TerrainChunkModelGeneration.java @@ -1,23 +1,18 @@ 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.Vector2f; import org.joml.Vector3f; import org.lwjgl.BufferUtils; import static org.lwjgl.opengl.GL30.glBindVertexArray; -import static org.lwjgl.opengl.GL30.glGenVertexArrays; import electrosphere.client.terrain.cells.VoxelTextureAtlas; import electrosphere.engine.Globals; @@ -25,14 +20,13 @@ import electrosphere.entity.types.terrain.TerrainChunkData; import electrosphere.renderer.model.Material; import electrosphere.renderer.model.Mesh; import electrosphere.renderer.model.Model; -import electrosphere.renderer.shader.ShaderProgram; import electrosphere.server.terrain.manager.ServerTerrainChunk; public class TerrainChunkModelGeneration { //http://paulbourke.net/geometry/polygonise/ - static int edgeTable[]={ + public 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, @@ -68,7 +62,7 @@ public class TerrainChunkModelGeneration { }; //256 by 16 - static int triTable[][] = { + public 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}, @@ -625,7 +619,7 @@ public class TerrainChunkModelGeneration { return new Vector3f(x,y,z); } - public static TerrainChunkData generateTerrainChunkData(float[][][] terrainGrid, int[][][] textureGrid){ + public static TerrainChunkData generateTerrainChunkData(float[][][] terrainGrid, int[][][] textureGrid, int lod){ // 5 6 // +-------------+ +-----5-------+ ^ Y diff --git a/src/main/java/electrosphere/renderer/meshgen/TransvoxelModelGeneration.java b/src/main/java/electrosphere/renderer/meshgen/TransvoxelModelGeneration.java index 1450278b..606b8d46 100644 --- a/src/main/java/electrosphere/renderer/meshgen/TransvoxelModelGeneration.java +++ b/src/main/java/electrosphere/renderer/meshgen/TransvoxelModelGeneration.java @@ -1,190 +1,29 @@ 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.Vector3f; -import org.lwjgl.BufferUtils; import electrosphere.engine.Globals; import electrosphere.entity.types.terrain.TerrainChunkData; -import electrosphere.renderer.meshgen.TerrainChunkModelGeneration.GridCell; -import electrosphere.renderer.meshgen.TerrainChunkModelGeneration.Triangle; -import electrosphere.renderer.model.Material; -import electrosphere.renderer.model.Mesh; -import electrosphere.renderer.model.Model; +import electrosphere.logger.LoggerInterface; +import electrosphere.server.terrain.manager.ServerTerrainChunk; /** * Utility functions for generating transvoxel based meshes */ public class TransvoxelModelGeneration { - // static class Triangle { - // int[] indices = new int[3]; //array of size 3 - - // public Triangle(int index0, int index1, int index2){ - // indices[0] = index0; - // indices[1] = index1; - // indices[2] = index2; - // } - // } - - // 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 verts, - // List normals, - // List trianglesSharingVert - // ){ - // 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) { - // // - // // Triangles calculation - // // - // //get indices - // Vector3f vert0 = vertList[triTable[cubeIndex][i+0]]; - // Vector3f vert1 = vertList[triTable[cubeIndex][i+1]]; - // Vector3f vert2 = vertList[triTable[cubeIndex][i+2]]; - // int index0 = getVertIndex(vert0,vertMap,verts); - // int index1 = getVertIndex(vert1,vertMap,verts); - // int index2 = getVertIndex(vert2,vertMap,verts); - - // //add 0's to normals until it matches vert count - // while(trianglesSharingVert.size() < verts.size()){ - // trianglesSharingVert.add(0); - // normals.add(new Vector3f()); - // } - // //add new triangle - // Triangle newTriangle = new Triangle(index0,index1,index2); - // triangles.add(newTriangle); - // ntriang++; - - - - // // - // // Normals calculation - // // - - - // //calculate normal for new triangle - // Vector3f u = verts.get(index1).sub(verts.get(index0), new Vector3f()); - // Vector3f v = verts.get(index2).sub(verts.get(index1), 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).normalize(); - - - - // //for each vertex, average the new normal with the normals that are already there - // int trianglesSharingIndex0 = trianglesSharingVert.get(index0); - // //calculate proportion of each normal - // float oldProportion = trianglesSharingIndex0 / (float)(trianglesSharingIndex0 + 1); - // float newProportion = 1.0f / (float)(trianglesSharingIndex0 + 1); - // //increment number of triangles sharing vert - // trianglesSharingVert.set(index0, trianglesSharingIndex0 + 1); - - // Vector3f currentNormal = normals.get(index0); - // currentNormal = averageNormals(currentNormal,oldProportion,n,newProportion); - // normals.get(index0).set(currentNormal); - - - - - - // int trianglesSharingIndex1 = trianglesSharingVert.get(index1); - // //calculate proportion of each normal - // oldProportion = trianglesSharingIndex1 / (float)(trianglesSharingIndex1 + 1); - // newProportion = 1.0f / (float)(trianglesSharingIndex1 + 1); - // //increment number of triangles sharing vert - // trianglesSharingVert.set(index1, trianglesSharingIndex1 + 1); - - // currentNormal = normals.get(index1); - // currentNormal = averageNormals(currentNormal,oldProportion,n,newProportion); - // normals.get(index1).set(currentNormal); + //this is the width of the transition cell + //it should be between 0 and 1, exclusive + //the higher the value, the more of the high resolution chunk we will see + //the lower the value, the more of the low resolution chunk we will see + static final float TRANSITION_CELL_WIDTH = 0.5f; @@ -193,179 +32,2457 @@ public class TransvoxelModelGeneration { - // int trianglesSharingIndex2 = trianglesSharingVert.get(index2); - // //calculate proportion of each normal - // oldProportion = trianglesSharingIndex2 / (float)(trianglesSharingIndex2 + 1); - // newProportion = 1.0f / (float)(trianglesSharingIndex2 + 1); - // //increment number of triangles sharing vert - // trianglesSharingVert.set(index2, trianglesSharingIndex2 + 1); + /** + * A single generated triangle + */ + static class Triangle { + //the indices + int[] indices = new int[3]; //array of size 3 - // currentNormal = normals.get(index2); - // currentNormal = averageNormals(currentNormal,oldProportion,n,newProportion); - // normals.get(index2).set(currentNormal); + /** + * Creates a triangle object + * @param index0 + * @param index1 + * @param index2 + */ + public Triangle(int index0, int index1, int index2){ + indices[0] = index0; + indices[1] = index1; + indices[2] = index2; + } + } - // } + /** + * The grid cell currently being looked at + */ + static class GridCell { + Vector3f[] points = new Vector3f[8]; //array of size 8 + double[] val = new double[8]; //array of size 8 + int[] atlasValues = new int[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, + int atl1, int atl2, int atl3, int atl4, + int atl5, int atl6, int atl7, int atl8 + ){ + //triangle points + points[0] = p1; points[1] = p2; points[2] = p3; points[3] = p4; + points[4] = p5; points[5] = p6; points[6] = p7; points[7] = p8; + //iso values + val[0] = val1; val[1] = val2; val[2] = val3; val[3] = val4; + val[4] = val5; val[5] = val6; val[6] = val7; val[7] = val8; + //atlas values + atlasValues[0] = atl1; atlasValues[1] = atl2; atlasValues[2] = atl3; atlasValues[3] = atl4; + atlasValues[4] = atl5; atlasValues[5] = atl6; atlasValues[6] = atl7; atlasValues[7] = atl8; + } + } - // return(ntriang); - // } + /** + * The transition grid cell currently being looked at + */ + static class TransitionGridCell { + Vector3f[][] complexFacePoints = new Vector3f[3][3]; + Vector3f[][] simpleFacePoints = new Vector3f[2][2]; + float[][] complexFaceValues = new float[3][3]; + float[][] simpleFaceValues = new float[2][2]; + int[][] complexFaceAtlasValues = new int[3][3]; + int[][] simpleFaceAtlasValues = new int[2][2]; + public void setValues( + Vector3f c1, Vector3f c2, Vector3f c3, + Vector3f c4, Vector3f c5, Vector3f c6, + Vector3f c7, Vector3f c8, Vector3f c9, - // //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; + Vector3f s1, Vector3f s2, + Vector3f s3, Vector3f s4, - // 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)); + float cVal1, float cVal2, float cVal3, + float cVal4, float cVal5, float cVal6, + float cVal7, float cVal8, float cVal9, - // return new Vector3f(x,y,z); - // } + float sVal1, float sVal2, + float sVal3, float sVal4, - // /** - // * Creates terrain chunk data based on data provided - // * @param terrainGrid the grid of weights to generate off of - // * @param textureGrid the grid of texture indices to generate off of - // * @param neighborLODLevels The lod levels of all neighbors and this cell. Should be 3x3x3 array of lod level values. - // */ - // public static TerrainChunkData generateTerrainChunkData(float[][][] terrainGrid, int[][][] textureGrid, int[][][] neighborLODLevels){ + int cA1, int cA2, int cA3, int cA4, + int cA5, int cA6, int cA7, int cA8, + int cA9, - // // 5 6 - // // +-------------+ +-----5-------+ ^ Y - // // / | / | / | /| | _ - // // / | / | 4 9 6 10 | /\ Z - // // 4 +-----+-------+ 7 | +-----+7------+ | | / - // // | 1 +-------+-----+ 2 | +-----1-+-----+ | / - // // | / | / 8 0 11 2 | / - // // | / | / | / | / |/ - // // 0 +-------------+ 3 +------3------+ +---------------> X + int sA1, int sA2, int sA3, int sA4 + ){ + //triangle points + complexFacePoints[0][0] = c1; complexFacePoints[0][1] = c2; complexFacePoints[0][2] = c3; + complexFacePoints[1][0] = c4; complexFacePoints[1][1] = c5; complexFacePoints[1][2] = c6; + complexFacePoints[2][0] = c7; complexFacePoints[2][1] = c8; complexFacePoints[2][2] = c9; + simpleFacePoints[0][0] = s1; simpleFacePoints[0][1] = s2; + simpleFacePoints[1][0] = s3; simpleFacePoints[1][1] = s4; + //iso values + complexFaceValues[0][0] = cVal1; complexFaceValues[0][1] = cVal2; complexFaceValues[0][2] = cVal3; + complexFaceValues[1][0] = cVal4; complexFaceValues[1][1] = cVal5; complexFaceValues[1][2] = cVal6; + complexFaceValues[2][0] = cVal7; complexFaceValues[2][1] = cVal8; complexFaceValues[2][2] = cVal9; + simpleFaceValues[0][0] = sVal1; simpleFaceValues[0][1] = sVal2; + simpleFaceValues[1][0] = sVal3; simpleFaceValues[1][1] = sVal4; + //atlas values + complexFaceAtlasValues[0][0] = cA1; complexFaceAtlasValues[0][1] = cA2; complexFaceAtlasValues[0][2] = cA3; + complexFaceAtlasValues[1][0] = cA4; complexFaceAtlasValues[1][1] = cA5; complexFaceAtlasValues[1][2] = cA6; + complexFaceAtlasValues[2][0] = cA7; complexFaceAtlasValues[2][1] = cA8; complexFaceAtlasValues[2][2] = cA9; + simpleFaceAtlasValues[0][0] = sA1; simpleFaceAtlasValues[0][1] = sA2; + simpleFaceAtlasValues[1][0] = sA3; simpleFaceAtlasValues[1][1] = sA4; + } + } - // //the current grid cell - // GridCell currentCell = new GridCell(); - // //the list of all triangles - // List triangles = new LinkedList(); - // //the map of vertex to index - // Map vertMap = new HashMap(); - // //the list of all verts - // List verts = new LinkedList(); - // //the list of all normals - // List normals = new LinkedList(); - // //the list of number of triangles that share a vert - // List trianglesSharingVert = new LinkedList(); - // //List of elements in order - // List faceElements = new LinkedList(); - // //List of UVs - // List UVs = new LinkedList(); + + + + + + + + + //location of the sampler index data in the shader + public static final int SAMPLER_INDEX_ATTRIB_LOC = 5; + //the ratio vectors of how much to pull from each texture + public static final int SAMPLER_RATIO_ATTRIB_LOC = 6; + + + + + + + + + + + /** + * Creates polygons for a voxel that is in the interior of this mesh + * @param grid + * @param isolevel + * @param triangles + * @param samplerIndices + * @param vertMap + * @param verts + * @param normals + * @param trianglesSharingVert + * @return + */ + protected static int polygonize( + GridCell grid, + double isolevel, + List triangles, + List samplerIndices, + Map vertMap, + List verts, + List normals, + List trianglesSharingVert + ){ + int i; + int ntriang; + int cubeIndex = 0; + Vector3f[] vertList = new Vector3f[12]; + int[] samplerIndex = new int[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 (TerrainChunkModelGeneration.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((TerrainChunkModelGeneration.edgeTable[cubeIndex] & 1) > 0){ + vertList[0] = VertexInterp(isolevel,grid.points[0],grid.points[1],grid.val[0],grid.val[1]); + } + if((TerrainChunkModelGeneration.edgeTable[cubeIndex] & 2) > 0){ + vertList[1] = VertexInterp(isolevel,grid.points[1],grid.points[2],grid.val[1],grid.val[2]); + } + if((TerrainChunkModelGeneration.edgeTable[cubeIndex] & 4) > 0){ + vertList[2] = VertexInterp(isolevel,grid.points[2],grid.points[3],grid.val[2],grid.val[3]); + } + if((TerrainChunkModelGeneration.edgeTable[cubeIndex] & 8) > 0){ + vertList[3] = VertexInterp(isolevel,grid.points[3],grid.points[0],grid.val[3],grid.val[0]); + } + if((TerrainChunkModelGeneration.edgeTable[cubeIndex] & 16) > 0){ + vertList[4] = VertexInterp(isolevel,grid.points[4],grid.points[5],grid.val[4],grid.val[5]); + } + if((TerrainChunkModelGeneration.edgeTable[cubeIndex] & 32) > 0){ + vertList[5] = VertexInterp(isolevel,grid.points[5],grid.points[6],grid.val[5],grid.val[6]); + } + if((TerrainChunkModelGeneration.edgeTable[cubeIndex] & 64) > 0){ + vertList[6] = VertexInterp(isolevel,grid.points[6],grid.points[7],grid.val[6],grid.val[7]); + } + if((TerrainChunkModelGeneration.edgeTable[cubeIndex] & 128) > 0){ + vertList[7] = VertexInterp(isolevel,grid.points[7],grid.points[4],grid.val[7],grid.val[4]); + } + if((TerrainChunkModelGeneration.edgeTable[cubeIndex] & 256) > 0){ + vertList[8] = VertexInterp(isolevel,grid.points[0],grid.points[4],grid.val[0],grid.val[4]); + } + if((TerrainChunkModelGeneration.edgeTable[cubeIndex] & 512) > 0){ + vertList[9] = VertexInterp(isolevel,grid.points[1],grid.points[5],grid.val[1],grid.val[5]); + } + if((TerrainChunkModelGeneration.edgeTable[cubeIndex] & 1024) > 0){ + vertList[10] = VertexInterp(isolevel,grid.points[2],grid.points[6],grid.val[2],grid.val[6]); + } + if((TerrainChunkModelGeneration.edgeTable[cubeIndex] & 2048) > 0){ + vertList[11] = VertexInterp(isolevel,grid.points[3],grid.points[7],grid.val[3],grid.val[7]); + } + + if(grid.val[0] > isolevel){ samplerIndex[0] = 0; } else { samplerIndex[0] = 1; } + if(grid.val[1] > isolevel){ samplerIndex[1] = 1; } else { samplerIndex[1] = 2; } + if(grid.val[2] > isolevel){ samplerIndex[2] = 2; } else { samplerIndex[2] = 3; } + if(grid.val[3] > isolevel){ samplerIndex[3] = 3; } else { samplerIndex[3] = 0; } + if(grid.val[4] > isolevel){ samplerIndex[4] = 4; } else { samplerIndex[4] = 5; } + if(grid.val[5] > isolevel){ samplerIndex[5] = 5; } else { samplerIndex[5] = 6; } + if(grid.val[6] > isolevel){ samplerIndex[6] = 6; } else { samplerIndex[6] = 7; } + if(grid.val[7] > isolevel){ samplerIndex[7] = 7; } else { samplerIndex[7] = 4; } + if(grid.val[0] > isolevel){ samplerIndex[8] = 0; } else { samplerIndex[8] = 4; } + if(grid.val[1] > isolevel){ samplerIndex[9] = 1; } else { samplerIndex[9] = 5; } + if(grid.val[2] > isolevel){ samplerIndex[10] = 2; } else { samplerIndex[10] = 6; } + if(grid.val[3] > isolevel){ samplerIndex[11] = 3; } else { samplerIndex[11] = 7; } + + //Create the triangle + ntriang = 0; + for (i=0; TerrainChunkModelGeneration.triTable[cubeIndex][i]!=-1; i+=3) { + // + // Triangles calculation + // + //get indices + Vector3f vert0 = vertList[TerrainChunkModelGeneration.triTable[cubeIndex][i+0]]; + Vector3f vert1 = vertList[TerrainChunkModelGeneration.triTable[cubeIndex][i+1]]; + Vector3f vert2 = vertList[TerrainChunkModelGeneration.triTable[cubeIndex][i+2]]; + int index0 = getVertIndex(vert0,vertMap,verts); + int index1 = getVertIndex(vert1,vertMap,verts); + int index2 = getVertIndex(vert2,vertMap,verts); + + //add 0's to normals until it matches vert count + while(trianglesSharingVert.size() < verts.size()){ + trianglesSharingVert.add(0); + normals.add(new Vector3f()); + } + + + //add new triangle + Triangle newTriangle = new Triangle(index0,index1,index2); + triangles.add(newTriangle); + ntriang++; + + + + + // + //Sampler triangles + // + for(int j = 0; j < 3; j++){ + //we add the triangle three times so all three vertices have the same values + //that way they don't interpolate when you're in a middle point of the fragment + //this could eventually potentially be optimized to send 1/3rd the data, but + //the current approach is easier to reason about + Vector3f samplerTriangle = new Vector3f( + grid.atlasValues[samplerIndex[TerrainChunkModelGeneration.triTable[cubeIndex][i+0]]], + grid.atlasValues[samplerIndex[TerrainChunkModelGeneration.triTable[cubeIndex][i+1]]], + grid.atlasValues[samplerIndex[TerrainChunkModelGeneration.triTable[cubeIndex][i+2]]] + ); + samplerIndices.add(samplerTriangle); + } + + + + + // + // Normals calculation + // + + + //calculate normal for new triangle + Vector3f u = verts.get(index1).sub(verts.get(index0), new Vector3f()); + Vector3f v = verts.get(index2).sub(verts.get(index1), 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).normalize(); + + + + //for each vertex, average the new normal with the normals that are already there + int trianglesSharingIndex0 = trianglesSharingVert.get(index0); + //calculate proportion of each normal + float oldProportion = trianglesSharingIndex0 / (float)(trianglesSharingIndex0 + 1); + float newProportion = 1.0f / (float)(trianglesSharingIndex0 + 1); + //increment number of triangles sharing vert + trianglesSharingVert.set(index0, trianglesSharingIndex0 + 1); + + Vector3f currentNormal = normals.get(index0); + currentNormal = averageNormals(currentNormal,oldProportion,n,newProportion); + normals.get(index0).set(currentNormal); + + + + + + int trianglesSharingIndex1 = trianglesSharingVert.get(index1); + //calculate proportion of each normal + oldProportion = trianglesSharingIndex1 / (float)(trianglesSharingIndex1 + 1); + newProportion = 1.0f / (float)(trianglesSharingIndex1 + 1); + //increment number of triangles sharing vert + trianglesSharingVert.set(index1, trianglesSharingIndex1 + 1); + + currentNormal = normals.get(index1); + currentNormal = averageNormals(currentNormal,oldProportion,n,newProportion); + normals.get(index1).set(currentNormal); + + + + + + + + + int trianglesSharingIndex2 = trianglesSharingVert.get(index2); + //calculate proportion of each normal + oldProportion = trianglesSharingIndex2 / (float)(trianglesSharingIndex2 + 1); + newProportion = 1.0f / (float)(trianglesSharingIndex2 + 1); + //increment number of triangles sharing vert + trianglesSharingVert.set(index2, trianglesSharingIndex2 + 1); + + currentNormal = normals.get(index2); + currentNormal = averageNormals(currentNormal,oldProportion,n,newProportion); + normals.get(index2).set(currentNormal); + + } + + return(ntriang); + } + + + /** + * Generates a transition cell + * @param grid The main grid cell + * @param highresFaceValues The values along the face that are double-resolution + * @param isolevel The iso level + * @param triangles The triangles buffer + * @param samplerIndices The Sampler indices buffer + * @param vertMap The vert map + * @param verts The list of verts + * @param normals The normal map + * @param trianglesSharingVert The indices of triangles sharing verts + * @return The number of triangles created + */ + protected static int polygonizeTransition( + TransitionGridCell transitionCell, + double isolevel, + List triangles, + List samplerIndices, + Map vertMap, + List verts, + List normals, + List trianglesSharingVert + ){ + + + /** + + + For a transition face, the face vertices are numbered differently + + complex face numbering: + + 6 7 8 + +-------+-------+ + | | | + | | | + | |4 | + 3+-------+-------+ 5 + | | | + | | | + | | | + +-------+-------+ + 0 1 2 + + + + + non complex face numbering: + + B C + +---------------+ + | | + | | + | | + | | + | | + | | + | | + +---------------+ + 9 A + + + + Coordinage system: + + Y + ^ + | + | + | + +---------> X + + + + + */ + + + + + + int i; + int ntriang; + int caseIndex = 0; + Vector3f[] vertList = new Vector3f[12]; + int[] samplerIndex = new int[12]; + + + + /** + The case index is constructed by using the values of the high-resolution face. + We assign each of the vertices the following values: + + 40 20 10 + +-------+-------+ + | | | + | | | + | |100 | + 80+-------+-------+ 08 + | | | + | | | + | | | + +-------+-------+ + 01 02 04 + + */ + + //get lookup key (index) for edge table + //edge table tells us which vertices are inside of the surface + if (transitionCell.complexFaceValues[0][0] < isolevel) caseIndex |= 1; + if (transitionCell.complexFaceValues[1][0] < isolevel) caseIndex |= 2; + if (transitionCell.complexFaceValues[2][0] < isolevel) caseIndex |= 4; + if (transitionCell.complexFaceValues[2][1] < isolevel) caseIndex |= 8; + if (transitionCell.complexFaceValues[2][2] < isolevel) caseIndex |= 16; + if (transitionCell.complexFaceValues[1][2] < isolevel) caseIndex |= 32; + if (transitionCell.complexFaceValues[0][2] < isolevel) caseIndex |= 64; + if (transitionCell.complexFaceValues[0][1] < isolevel) caseIndex |= 128; + if (transitionCell.complexFaceValues[1][1] < isolevel) caseIndex |= 256; + + //Cube is entirely in/out of the surface + if(caseIndex == 0 || caseIndex == 511){ + return(0); + } + + + //the class of transition cell + short cellClass = (short)(transitionCellClass[caseIndex]); + + LoggerInterface.loggerRenderer.DEBUG("Cell class: " + cellClass + " " + String.format("0x%02X", cellClass)); + + + /** + + The complex edges that a vertex can lie on are numbered as follows: + + 83 84 + +-------+-------+ + | | | + 16| |46 |86 + | 43 | 44 | + +-------+-------+ + | | | + 15| |45 |85 + | | | + +-------+-------+ + 23 24 + + + + The simple edges that a vertex can lie on are numbered as follows: + + 88 + +---------------+ + | | + | | + | | + 19| |89 + | | + | | + | | + +---------------+ + 28 + + + + */ + + + /* + + + The sample locations are assigned an 8 bit code + + + Complex face: + + + 10 81 80 + +-------+-------+ + | | | + | | | + | |40 | + 12+-------+-------+ 82 + | | | + | | | + | | | + +-------+-------+ + 30 21 20 + + + Simple face: + + 17 87 + +---------------+ + | | + | | + | | + | | + | | + | | + | | + +---------------+ + 37 27 + + + + + + + */ + + + // + // + // Calculate the locations of all vertices for this cube + // + // + + int[] vertexData = transitionVertexData[caseIndex]; + + //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(vertexData.length > 0){ + for(i = 0; i < vertexData.length; i++){ + //get into from transvoxel tables + + //contains the corner indexes of the edge's endpoints in one nibble each + short lowByte = (short)(vertexData[i] & 0xFF); + + LoggerInterface.loggerRenderer.DEBUG("Low byte: "); + LoggerInterface.loggerRenderer.DEBUG(String.format("0x%02X", lowByte)); + + //contains the vertex reuse data + //the bit values 1 and 2 indicate we should subtract one from the x or y coordinate respectively + //they're never simultaneously set + //bit 4 indicates a new vertex is to be created on an interior edge were we cannot reuse + //bit 8 indicates that a new vertex is created on the maximal edge where it can be reused + short highByte = (short)(vertexData[i] >> 8 & 0xFF); + // int subX = highByte & 0x01; + // int subY = highByte & 0x02; + int newInteriorVertex = highByte & 0x04; + // int vertexCanBeReused = highByte & 0x08; + + LoggerInterface.loggerRenderer.DEBUG("High byte: "); + LoggerInterface.loggerRenderer.DEBUG(String.format("0x%02X", highByte)); + + if(newInteriorVertex > 0){ + LoggerInterface.loggerRenderer.DEBUG("New interior Vertex"); + } + + //the corner indices to sample + int firstCornerSampleIndex = (int)(lowByte >> 4 & 0xF); + int secondCornerSampleIndex = (int)(lowByte & 0xF); + + LoggerInterface.loggerRenderer.DEBUG("Corner indices: " + firstCornerSampleIndex + " " + secondCornerSampleIndex); + + //get the iso sample values + float firstSample = getTransvoxelSampleValue(transitionCell.simpleFaceValues,transitionCell.complexFaceValues,firstCornerSampleIndex); + float secondSample = getTransvoxelSampleValue(transitionCell.simpleFaceValues,transitionCell.complexFaceValues,secondCornerSampleIndex); + + // + //Sample check -- we should never be interpolating between two samples of 0 value + if(firstSample <= 0 && secondSample <= 0){ + String message = "" + + transitionCell.complexFaceValues[0][2] + " " + transitionCell.complexFaceValues[1][2] + " " + transitionCell.complexFaceValues[2][2] + "\n" + + transitionCell.complexFaceValues[0][1] + " " + transitionCell.complexFaceValues[1][1] + " " + transitionCell.complexFaceValues[2][1] + "\n" + + transitionCell.complexFaceValues[0][0] + " " + transitionCell.complexFaceValues[1][0] + " " + transitionCell.complexFaceValues[2][0] + "\n" + + "\n" + + transitionCell.simpleFaceValues[0][1] + " " + transitionCell.simpleFaceValues[1][1] + "\n" + + transitionCell.simpleFaceValues[0][0] + " " + transitionCell.simpleFaceValues[1][0] + ; + LoggerInterface.loggerRenderer.ERROR(message, new IllegalStateException("Two samples in transvoxel algorithm are both zero -- can't interpolate between them!")); + } + + //get the vertices we're interpolating + //we need to map 0x0 through 0xC to the coordinates we're actually modifying + Vector3f firstVertex = getTransvoxelVectorByIndex(transitionCell,firstCornerSampleIndex); + Vector3f secondVertex = getTransvoxelVectorByIndex(transitionCell,secondCornerSampleIndex); + + //calculate interpolated vertex between the two samples such that it lies on the edge of the isosurface + vertList[i] = VertexInterp(isolevel,firstVertex,secondVertex,firstSample,secondSample); + + //figure out what sample we're pulling texture from + if(firstSample > isolevel){ + samplerIndex[i] = getTransvoxelTextureValue(transitionCell.simpleFaceAtlasValues,transitionCell.complexFaceAtlasValues,firstCornerSampleIndex); + } else { + samplerIndex[i] = sampleIndexTable[1][i]; + } + } + } + + + + + // + // + // Create the triangles using the vertices we just created + // + // + + //the triangle data + //basically, a list of indices into the vertex array where every three entries + //in triangleData correspondes to three vertices which we want to turn into a triangle + int lookupValue = cellClass & 0x7F; //per instruction, must be ANDed before lookup + LoggerInterface.loggerRenderer.DEBUG("Triangulation lookup value: " + lookupValue); + TransitionCellData triangleData = transitionCellData[lookupValue]; + + short vertexCount = (short)(triangleData.geometryCounts >> 4 & 0xF); + short triangleCount = (short)(triangleData.geometryCounts & 0xF); + LoggerInterface.loggerRenderer.DEBUG("Vertex Count: " + vertexCount + " Triangle Count: " + triangleCount); + + //Create the triangle + ntriang = 0; + for (i=0; i < triangleData.vertexIndex.length; i+=3) { + // + // Triangles calculation + // + //get indices -- these values are the same as the indices we sample from (refer to figure 4.19 for the listing) + char cornerReuseData0 = transitionCornerData[triangleData.vertexIndex[i+0]]; + char cornerReuseData1 = transitionCornerData[triangleData.vertexIndex[i+1]]; + char cornerReuseData2 = transitionCornerData[triangleData.vertexIndex[i+2]]; + LoggerInterface.loggerRenderer.DEBUG((int)triangleData.vertexIndex[i+0] + " " + (int)triangleData.vertexIndex[i+1] + " " + (int)triangleData.vertexIndex[i+2]); + LoggerInterface.loggerRenderer.DEBUG((int)cornerReuseData0 + " " + (int)cornerReuseData1 + " " + (int)cornerReuseData2); + Vector3f vert0 = vertList[triangleData.vertexIndex[i+0]]; + Vector3f vert1 = vertList[triangleData.vertexIndex[i+1]]; + Vector3f vert2 = vertList[triangleData.vertexIndex[i+2]]; + + LoggerInterface.loggerRenderer.DEBUG(vert0 + ""); + LoggerInterface.loggerRenderer.DEBUG(vert1 + ""); + LoggerInterface.loggerRenderer.DEBUG(vert2 + ""); + int index0 = getVertIndex(vert0,vertMap,verts); + int index1 = getVertIndex(vert1,vertMap,verts); + int index2 = getVertIndex(vert2,vertMap,verts); + + //add 0's to normals until it matches vert count + while(trianglesSharingVert.size() < verts.size()){ + trianglesSharingVert.add(0); + normals.add(new Vector3f()); + } + + + //add new triangle + Triangle newTriangle = new Triangle(index0,index1,index2); + triangles.add(newTriangle); + ntriang++; + + + + + // + //Sampler triangles + // + for(int j = 0; j < 3; j++){ + //we add the triangle three times so all three vertices have the same values + //that way they don't interpolate when you're in a middle point of the fragment + //this could eventually potentially be optimized to send 1/3rd the data, but + //the current approach is easier to reason about + Vector3f samplerTriangle = new Vector3f( + samplerIndex[triangleData.vertexIndex[i+0]], + samplerIndex[triangleData.vertexIndex[i+1]], + samplerIndex[triangleData.vertexIndex[i+2]] + ); + samplerIndices.add(samplerTriangle); + } + + + + + // + // Normals calculation + // + + + //calculate normal for new triangle + Vector3f u = verts.get(index1).sub(verts.get(index0), new Vector3f()); + Vector3f v = verts.get(index2).sub(verts.get(index1), 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).normalize(); + + + + //for each vertex, average the new normal with the normals that are already there + int trianglesSharingIndex0 = trianglesSharingVert.get(index0); + //calculate proportion of each normal + float oldProportion = trianglesSharingIndex0 / (float)(trianglesSharingIndex0 + 1); + float newProportion = 1.0f / (float)(trianglesSharingIndex0 + 1); + //increment number of triangles sharing vert + trianglesSharingVert.set(index0, trianglesSharingIndex0 + 1); + + Vector3f currentNormal = normals.get(index0); + currentNormal = averageNormals(currentNormal,oldProportion,n,newProportion); + normals.get(index0).set(currentNormal); + + + + + + int trianglesSharingIndex1 = trianglesSharingVert.get(index1); + //calculate proportion of each normal + oldProportion = trianglesSharingIndex1 / (float)(trianglesSharingIndex1 + 1); + newProportion = 1.0f / (float)(trianglesSharingIndex1 + 1); + //increment number of triangles sharing vert + trianglesSharingVert.set(index1, trianglesSharingIndex1 + 1); + + currentNormal = normals.get(index1); + currentNormal = averageNormals(currentNormal,oldProportion,n,newProportion); + normals.get(index1).set(currentNormal); + + + + + + + + + int trianglesSharingIndex2 = trianglesSharingVert.get(index2); + //calculate proportion of each normal + oldProportion = trianglesSharingIndex2 / (float)(trianglesSharingIndex2 + 1); + newProportion = 1.0f / (float)(trianglesSharingIndex2 + 1); + //increment number of triangles sharing vert + trianglesSharingVert.set(index2, trianglesSharingIndex2 + 1); + + currentNormal = normals.get(index2); + currentNormal = averageNormals(currentNormal,oldProportion,n,newProportion); + normals.get(index2).set(currentNormal); + + } + + return(ntriang); + } + + /** + * Sample grid values based on the index + * @param simpleEdge simple edge values + * @param complexEdgeValues complex edge values + * @param index index to sample at + * @return the iso value + */ + static float getTransvoxelSampleValue(float[][] simpleEdge, float[][] complexEdgeValues, int index){ + if(index < 9){ + //sample from complex edge + switch(index){ + case 0: + return complexEdgeValues[0][0]; + case 1: + return complexEdgeValues[1][0]; + case 2: + return complexEdgeValues[2][0]; + case 3: + return complexEdgeValues[0][1]; + case 4: + return complexEdgeValues[1][1]; + case 5: + return complexEdgeValues[2][1]; + case 6: + return complexEdgeValues[0][2]; + case 7: + return complexEdgeValues[1][2]; + case 8: + return complexEdgeValues[2][2]; + } + } else { + //sample from non-complex edge + switch(index){ + case 9: + return simpleEdge[0][0]; + case 10: + return simpleEdge[1][0]; + case 11: + return simpleEdge[0][1]; + case 12: + return simpleEdge[1][1]; + } + } + return 0.0f; + } + + /** + * Sample grid values based on the index + * @param simpleEdge simple edge values + * @param complexEdgeValues complex edge values + * @param index index to sample at + * @return the iso value + */ + static int getTransvoxelTextureValue(int[][] simpleEdge, int[][] complexEdgeValues, int index){ + if(index < 9){ + //sample from complex edge + switch(index){ + case 0: + return complexEdgeValues[0][0]; + case 1: + return complexEdgeValues[1][0]; + case 2: + return complexEdgeValues[2][0]; + case 3: + return complexEdgeValues[0][1]; + case 4: + return complexEdgeValues[1][1]; + case 5: + return complexEdgeValues[2][1]; + case 6: + return complexEdgeValues[0][2]; + case 7: + return complexEdgeValues[1][2]; + case 8: + return complexEdgeValues[2][2]; + } + } else { + //sample from non-complex edge + switch(index){ + case 9: + return simpleEdge[0][0]; + case 10: + return simpleEdge[1][0]; + case 11: + return simpleEdge[0][1]; + case 12: + return simpleEdge[1][1]; + } + } + return 0; + } + + /** + * Gets the vector from the transition grid cell based on its index + * @param transitionCell The transition cell + * @param index The index + * @return The vector + */ + static Vector3f getTransvoxelVectorByIndex(TransitionGridCell transitionCell, int index){ + if(index < 9){ + //sample from complex edge + switch(index){ + case 0: + return transitionCell.complexFacePoints[0][0]; + case 1: + return transitionCell.complexFacePoints[1][0]; + case 2: + return transitionCell.complexFacePoints[2][0]; + case 3: + return transitionCell.complexFacePoints[0][1]; + case 4: + return transitionCell.complexFacePoints[1][1]; + case 5: + return transitionCell.complexFacePoints[2][1]; + case 6: + return transitionCell.complexFacePoints[0][2]; + case 7: + return transitionCell.complexFacePoints[1][2]; + case 8: + return transitionCell.complexFacePoints[2][2]; + } + } else { + //sample from non-complex edge + switch(index){ + case 9: + return transitionCell.simpleFacePoints[0][0]; + case 10: + return transitionCell.simpleFacePoints[1][0]; + case 11: + return transitionCell.simpleFacePoints[0][1]; + case 12: + return transitionCell.simpleFacePoints[1][1]; + } + } + //index should never be greater than 12 as there are only 13 points we're concerned with per transition voxel + throw new UnknownError("Managed to index outside of expected range"); + } + + //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); + } + + //TODO: more optimal key creation + private static String getVertKeyFromPoints(float x, float y, float z){ + return x + "_" + y + "_" + z; + } + + /** + * Gets the already existing index of this point + * @param vert the vertex's raw position + * @param vertMap the map of key ->Vert index + * @param verts + * @return + */ + private static int getVertIndex(Vector3f vert, Map vertMap, List verts){ + int rVal = -1; + String vertKey = getVertKeyFromPoints(vert.x,vert.y,vert.z); + if(vertMap.containsKey(vertKey)){ + return vertMap.get(vertKey); + } else { + rVal = verts.size(); + verts.add(vert); + vertMap.put(vertKey,rVal); + return rVal; + } + } + + private static Vector3f averageNormals(Vector3f normal0, float proportion0, Vector3f normal1, float proportion1){ + Vector3f rVal = new Vector3f(normal0); + rVal = rVal.mul(proportion0).add(new Vector3f(normal1).mul(proportion1)); + return rVal; + } + + + /** + * Generates mesh data given chunk data + * @param terrainGrid The chunk data + * @param textureGrid The chunk texture data + * @return The mesh data + */ + public static TerrainChunkData generateTerrainChunkData(TransvoxelChunkData chunkData){ + + // 5 6 + // +-------------+ +-----5-------+ ^ Y + // / | / | / | /| | _ + // / | / | 4 9 6 10 | /\ Z + // 4 +-----+-------+ 7 | +-----+7------+ | | / + // | 1 +-------+-----+ 2 | +-----1-+-----+ | / + // | / | / 8 0 11 2 | / + // | / | / | / | / |/ + // 0 +-------------+ 3 +------3------+ +---------------> X + + + + + //the current grid cell + GridCell currentCell = new GridCell(); + //Transition grid cell + TransitionGridCell currentTransitionCell = new TransitionGridCell(); + //the list of all triangles + List triangles = new LinkedList(); + //the map of vertex to index + Map vertMap = new HashMap(); + //the list of all verts + List verts = new LinkedList(); + //the list of all normals + List normals = new LinkedList(); + //the list of number of triangles that share a vert + List trianglesSharingVert = new LinkedList(); + //List of texture sampler values + List samplerTriangles = new LinkedList(); + //List of UVs + List UVs = 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, verts, normals, trianglesSharingVert); - // } - // } - // } + // + //Generate the interior of the mesh + for(int x = 1; x < chunkData.terrainGrid.length - 2; x++){ + for(int y = 1; y < chunkData.terrainGrid[0].length - 2; y++){ + for(int z = 1; z < chunkData.terrainGrid[0][0].length - 2; 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), + chunkData.terrainGrid[x+0][y+0][z+0], chunkData.terrainGrid[x+0][y+0][z+1], chunkData.terrainGrid[x+1][y+0][z+1], chunkData.terrainGrid[x+1][y+0][z+0], + chunkData.terrainGrid[x+0][y+1][z+0], chunkData.terrainGrid[x+0][y+1][z+1], chunkData.terrainGrid[x+1][y+1][z+1], chunkData.terrainGrid[x+1][y+1][z+0], + chunkData.textureGrid[x+0][y+0][z+0], chunkData.textureGrid[x+0][y+0][z+1], chunkData.textureGrid[x+1][y+0][z+1], chunkData.textureGrid[x+1][y+0][z+0], + chunkData.textureGrid[x+0][y+1][z+0], chunkData.textureGrid[x+0][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+0] + ); + //polygonize the current gridcell + polygonize(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); + } + } + } - // //all verts in order, flattened as an array of floats instead of vecs - // List vertsFlat = new LinkedList(); - // //all normals in order, flattened as an array of floats instead of vecs - // List normalsFlat = new LinkedList(); - // //all elements of faces in order - // List elementsFlat = new LinkedList(); - - // //flatten verts + normals - // for(Vector3f vert : verts){ - // vertsFlat.add(vert.x); - // vertsFlat.add(vert.y); - // vertsFlat.add(vert.z); - // } - - // for(Vector3f normal : normals){ - // normalsFlat.add(normal.x); - // normalsFlat.add(normal.y); - // normalsFlat.add(normal.z); - // } + int chunkWidth = ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; - // for(Triangle triangle : triangles){ - // elementsFlat.add(triangle.indices[0]); - // elementsFlat.add(triangle.indices[1]); - // elementsFlat.add(triangle.indices[2]); - // } + LoggerInterface.loggerRenderer.DEBUG("Triangles prior to transition cells: " + triangles.size()); - // float[] temp = new float[3]; - // int i = 0; - // for(Vector3f normal : normals){ - // Vector3f vert = verts.get(i); - // float absX = Math.abs(normal.x); - // float absY = Math.abs(normal.y); - // float absZ = Math.abs(normal.z); + int xStartIndex = chunkData.xNegativeEdgeIso != null ? 1 : 0; + int xEndIndex = chunkData.xPositiveEdgeIso != null ? chunkWidth - 2 : chunkWidth - 1; + int yStartIndex = chunkData.yNegativeEdgeIso != null ? 1 : 0; + int yEndIndex = chunkData.yPositiveEdgeIso != null ? chunkWidth - 2 : chunkWidth - 1; + int zStartIndex = chunkData.zNegativeEdgeIso != null ? 1 : 0; + int zEndIndex = chunkData.zPositiveEdgeIso != null ? chunkWidth - 2 : chunkWidth - 1; - // float uvX = vert.z * absX + vert.x * absY + vert.x * absZ; - // float uvY = vert.y * absX + vert.z * absY + vert.y * absZ; - // temp[0] = uvX; - // temp[1] = uvY; + // + //generate the x-positive face + if(chunkData.xPositiveEdgeIso != null){ + int x = ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE - 2; + for(int y = yStartIndex; y < yEndIndex - 1; y++){ + for(int z = zStartIndex; z < zEndIndex - 1; z++){ + // + //Generate the transition cell + // + currentTransitionCell.setValues( + //complex face vertex coordinates + new Vector3f(x+1,y,z), new Vector3f(x+1,y+TRANSITION_CELL_WIDTH,z), new Vector3f(x+1,y+1,z), + new Vector3f(x+1,y,z+TRANSITION_CELL_WIDTH), new Vector3f(x+1,y+TRANSITION_CELL_WIDTH,z+TRANSITION_CELL_WIDTH), new Vector3f(x+1,y+1,z+TRANSITION_CELL_WIDTH), + new Vector3f(x+1,y,z+1), new Vector3f(x+1,y+TRANSITION_CELL_WIDTH,z+1), new Vector3f(x+1,y+1,z+1), + //simple face vertex coordinates + new Vector3f(x+TRANSITION_CELL_WIDTH,y,z), new Vector3f(x+TRANSITION_CELL_WIDTH,y+1,z), + new Vector3f(x+TRANSITION_CELL_WIDTH,y,z+1), new Vector3f(x+TRANSITION_CELL_WIDTH,y+1,z+1), + //complex face iso values + chunkData.xPositiveEdgeIso[(y+0)*2+0][(z+0)*2+0], chunkData.xPositiveEdgeIso[(y+0)*2+1][(z+0)*2+0], chunkData.xPositiveEdgeIso[(y+1)*2+0][(z+0)*2+0], + chunkData.xPositiveEdgeIso[(y+0)*2+0][(z+0)*2+1], chunkData.xPositiveEdgeIso[(y+0)*2+1][(z+0)*2+1], chunkData.xPositiveEdgeIso[(y+1)*2+0][(z+0)*2+1], + chunkData.xPositiveEdgeIso[(y+0)*2+0][(z+1)*2+0], chunkData.xPositiveEdgeIso[(y+0)*2+1][(z+1)*2+0], chunkData.xPositiveEdgeIso[(y+1)*2+0][(z+1)*2+0], + //simple face iso values + chunkData.xPositiveEdgeIso[(y+0)*2+0][(z+0)*2+0], chunkData.xPositiveEdgeIso[(y+1)*2+0][(z+0)*2+0], + chunkData.xPositiveEdgeIso[(y+0)*2+0][(z+1)*2+0], chunkData.xPositiveEdgeIso[(y+1)*2+0][(z+1)*2+0], + //complex face texture atlas values + chunkData.xPositiveEdgeAtlas[(y+0)*2+0][(z+0)*2+0], chunkData.xPositiveEdgeAtlas[(y+0)*2+1][(z+0)*2+0], chunkData.xPositiveEdgeAtlas[(y+1)*2+0][(z+0)*2+0], + chunkData.xPositiveEdgeAtlas[(y+0)*2+0][(z+0)*2+1], chunkData.xPositiveEdgeAtlas[(y+0)*2+1][(z+0)*2+1], chunkData.xPositiveEdgeAtlas[(y+1)*2+0][(z+0)*2+1], + chunkData.xPositiveEdgeAtlas[(y+0)*2+0][(z+1)*2+0], chunkData.xPositiveEdgeAtlas[(y+0)*2+1][(z+1)*2+0], chunkData.xPositiveEdgeAtlas[(y+1)*2+0][(z+1)*2+0], + //simple face texture atlas values + chunkData.xPositiveEdgeAtlas[(y+0)*2+0][(z+0)*2+0], chunkData.xPositiveEdgeAtlas[(y+1)*2+0][(z+0)*2+0], + chunkData.xPositiveEdgeAtlas[(y+0)*2+0][(z+1)*2+0], chunkData.xPositiveEdgeAtlas[(y+1)*2+0][(z+1)*2+0] + ); + polygonizeTransition(currentTransitionCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); - // // if(absX >= absZ && absX >= absY){ - // // temp[0] = normal.z / 2.0f + 0.5f + vert.z * (absX / (absX + absZ)) + vert.x * (absZ / (absX + absZ)); - // // temp[1] = normal.y / 2.0f + 0.5f + vert.x * (absY / (absX + absY)) + vert.y * (absX / (absX + absY)); - // // } else if(absZ >= absX && absZ >= absY){ - // // temp[0] = normal.x / 2.0f + 0.5f + vert.z * (absX / (absX + absZ)) + vert.x * (absZ / (absX + absZ)); - // // temp[1] = normal.y / 2.0f + 0.5f + vert.z * (absY / (absZ + absY)) + vert.y * (absZ / (absZ + absY)); - // // } else if(absY >= absX && absY >= absZ){ - // // temp[0] = normal.x / 2.0f + 0.5f + vert.y * (absX / (absX + absY)) + vert.x * (absY / (absX + absY)); - // // temp[1] = normal.z / 2.0f + 0.5f + vert.y * (absZ / (absZ + absY)) + vert.z * (absY / (absZ + absY)); - // // } else { - // // temp[0] = vert.x / 1.5f + vert.z / 1.5f; - // // temp[1] = vert.y / 1.5f + vert.z / 1.5f; - // // } - // i++; - // UVs.add(temp[0]); - // UVs.add(temp[1]); - // } + // + //Generate the normal cell with half width + // + currentCell.setValues( + new Vector3f(x+0,y+0,z+0), new Vector3f(x+0,y+0,z+1), new Vector3f(x+TRANSITION_CELL_WIDTH,y+0,z+1), new Vector3f(x+TRANSITION_CELL_WIDTH,y+0,z+0), + new Vector3f(x+0,y+1,z+0), new Vector3f(x+0,y+1,z+1), new Vector3f(x+TRANSITION_CELL_WIDTH,y+1,z+1), new Vector3f(x+TRANSITION_CELL_WIDTH,y+1,z+0), + chunkData.terrainGrid[x+0][y+0][z+0], chunkData.terrainGrid[x+0][y+0][z+1], chunkData.xPositiveEdgeIso[(y+0)*2+0][(z+1)*2+0], chunkData.xPositiveEdgeIso[(y+0)*2+0][(z+0)*2+0], + chunkData.terrainGrid[x+0][y+1][z+0], chunkData.terrainGrid[x+0][y+1][z+1], chunkData.xPositiveEdgeIso[(y+1)*2+0][(z+1)*2+0], chunkData.xPositiveEdgeIso[(y+1)*2+0][(z+0)*2+0], + chunkData.textureGrid[x+0][y+0][z+0], chunkData.textureGrid[x+0][y+0][z+1], chunkData.xPositiveEdgeAtlas[(y+0)*2+0][(z+1)*2+0], chunkData.xPositiveEdgeAtlas[(y+0)*2+0][(z+0)*2+0], + chunkData.textureGrid[x+0][y+1][z+0], chunkData.textureGrid[x+0][y+1][z+1], chunkData.xPositiveEdgeAtlas[(y+1)*2+0][(z+1)*2+0], chunkData.xPositiveEdgeAtlas[(y+1)*2+0][(z+0)*2+0] + ); + //polygonize the current gridcell + polygonize(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); + } + } + } else { + int x = ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE - 2; + for(int y = yStartIndex; y < yEndIndex; y++){ + for(int z = zStartIndex; z < zEndIndex; 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), + chunkData.terrainGrid[x+0][y+0][z+0], chunkData.terrainGrid[x+0][y+0][z+1], chunkData.terrainGrid[x+1][y+0][z+1], chunkData.terrainGrid[x+1][y+0][z+0], + chunkData.terrainGrid[x+0][y+1][z+0], chunkData.terrainGrid[x+0][y+1][z+1], chunkData.terrainGrid[x+1][y+1][z+1], chunkData.terrainGrid[x+1][y+1][z+0], + chunkData.textureGrid[x+0][y+0][z+0], chunkData.textureGrid[x+0][y+0][z+1], chunkData.textureGrid[x+1][y+0][z+1], chunkData.textureGrid[x+1][y+0][z+0], + chunkData.textureGrid[x+0][y+1][z+0], chunkData.textureGrid[x+0][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+0] + ); + //polygonize the current gridcell + polygonize(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); + } + } + } - // //List vertices, List normals, List faceElements, List uvs - // TerrainChunkData rVal = new TerrainChunkData(vertsFlat, normalsFlat, elementsFlat, UVs); - // return rVal; - // } + LoggerInterface.loggerRenderer.DEBUG("Triangles after transition cells: " + triangles.size()); - // private static String getVertKeyFromPoints(float x, float y, float z){ - // return x + "_" + y + "_" + z; - // } - // private static int getVertIndex(Vector3f vert, Map vertMap, List verts){ - // int rVal = -1; - // String vertKey = getVertKeyFromPoints(vert.x,vert.y,vert.z); - // if(vertMap.containsKey(vertKey)){ - // return vertMap.get(vertKey); - // } else { - // rVal = verts.size(); - // verts.add(vert); - // vertMap.put(vertKey,rVal); - // return rVal; - // } - // } - // private static Vector3f averageNormals(Vector3f normal0, float proportion0, Vector3f normal1, float proportion1){ - // Vector3f rVal = new Vector3f(normal0); - // rVal = rVal.mul(proportion0).add(new Vector3f(normal1).mul(proportion1)); - // return rVal; - // } + + + // + //generate the x-negative face + if(chunkData.xNegativeEdgeIso != null){ + int x = 0; + for(int y = yStartIndex; y < yEndIndex - 1; y++){ + for(int z = zStartIndex; z < zEndIndex - 1; z++){ + // + //Generate the transition cell + // + currentTransitionCell.setValues( + //complex face vertex coordinates + new Vector3f(x,y,z), new Vector3f(x,y+TRANSITION_CELL_WIDTH,z), new Vector3f(x,y+1,z), + new Vector3f(x,y,z+TRANSITION_CELL_WIDTH), new Vector3f(x,y+TRANSITION_CELL_WIDTH,z+TRANSITION_CELL_WIDTH), new Vector3f(x,y+1,z+TRANSITION_CELL_WIDTH), + new Vector3f(x,y,z+1), new Vector3f(x,y+TRANSITION_CELL_WIDTH,z+1), new Vector3f(x,y+1,z+1), + //simple face vertex coordinates + new Vector3f(x+TRANSITION_CELL_WIDTH,y,z), new Vector3f(x+TRANSITION_CELL_WIDTH,y+1,z), + new Vector3f(x+TRANSITION_CELL_WIDTH,y,z+1), new Vector3f(x+TRANSITION_CELL_WIDTH,y+1,z+1), + //complex face iso values + chunkData.xNegativeEdgeIso[(y+0)*2+0][(z+0)*2+0], chunkData.xNegativeEdgeIso[(y+0)*2+1][(z+0)*2+0], chunkData.xNegativeEdgeIso[(y+1)*2+0][(z+0)*2+0], + chunkData.xNegativeEdgeIso[(y+0)*2+0][(z+0)*2+1], chunkData.xNegativeEdgeIso[(y+0)*2+1][(z+0)*2+1], chunkData.xNegativeEdgeIso[(y+1)*2+0][(z+0)*2+1], + chunkData.xNegativeEdgeIso[(y+0)*2+0][(z+1)*2+0], chunkData.xNegativeEdgeIso[(y+0)*2+1][(z+1)*2+0], chunkData.xNegativeEdgeIso[(y+1)*2+0][(z+1)*2+0], + //simple face iso values + chunkData.xNegativeEdgeIso[(y+0)*2+0][(z+0)*2+0], chunkData.xNegativeEdgeIso[(y+1)*2+0][(z+0)*2+0], + chunkData.xNegativeEdgeIso[(y+0)*2+0][(z+1)*2+0], chunkData.xNegativeEdgeIso[(y+1)*2+0][(z+1)*2+0], + //complex face texture atlas values + chunkData.xNegativeEdgeAtlas[(y+0)*2+0][(z+0)*2+0], chunkData.xNegativeEdgeAtlas[(y+0)*2+1][(z+0)*2+0], chunkData.xNegativeEdgeAtlas[(y+1)*2+0][(z+0)*2+0], + chunkData.xNegativeEdgeAtlas[(y+0)*2+0][(z+0)*2+1], chunkData.xNegativeEdgeAtlas[(y+0)*2+1][(z+0)*2+1], chunkData.xNegativeEdgeAtlas[(y+1)*2+0][(z+0)*2+1], + chunkData.xNegativeEdgeAtlas[(y+0)*2+0][(z+1)*2+0], chunkData.xNegativeEdgeAtlas[(y+0)*2+1][(z+1)*2+0], chunkData.xNegativeEdgeAtlas[(y+1)*2+0][(z+1)*2+0], + //simple face texture atlas values + chunkData.xNegativeEdgeAtlas[(y+0)*2+0][(z+0)*2+0], chunkData.xNegativeEdgeAtlas[(y+1)*2+0][(z+0)*2+0], + chunkData.xNegativeEdgeAtlas[(y+0)*2+0][(z+1)*2+0], chunkData.xNegativeEdgeAtlas[(y+1)*2+0][(z+1)*2+0] + ); + polygonizeTransition(currentTransitionCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); + + // + //Generate the normal cell with half width + // + currentCell.setValues( + new Vector3f(x+TRANSITION_CELL_WIDTH,y+0,z+0), new Vector3f(x+TRANSITION_CELL_WIDTH,y+0,z+1), new Vector3f(x+1,y+0,z+1), new Vector3f(x+1,y+0,z+0), + new Vector3f(x+TRANSITION_CELL_WIDTH,y+1,z+0), new Vector3f(x+TRANSITION_CELL_WIDTH,y+1,z+1), new Vector3f(x+1,y+1,z+1), new Vector3f(x+1,y+1,z+0), + chunkData.xNegativeEdgeIso[(y+0)*2+0][(z+0)*2+0], chunkData.xNegativeEdgeIso[(y+0)*2+0][(z+1)*2+0], chunkData.terrainGrid[x+1][y+0][z+1], chunkData.terrainGrid[x+1][y+0][z+0], + chunkData.xNegativeEdgeIso[(y+1)*2+0][(z+0)*2+0], chunkData.xNegativeEdgeIso[(y+1)*2+0][(z+1)*2+0], chunkData.terrainGrid[x+1][y+1][z+1], chunkData.terrainGrid[x+1][y+1][z+0], + chunkData.xNegativeEdgeAtlas[(y+0)*2+0][(z+0)*2+0], chunkData.xNegativeEdgeAtlas[(y+0)*2+0][(z+1)*2+0], chunkData.textureGrid[x+1][y+0][z+1], chunkData.textureGrid[x+1][y+0][z+0], + chunkData.xNegativeEdgeAtlas[(y+1)*2+0][(z+0)*2+0], chunkData.xNegativeEdgeAtlas[(y+1)*2+0][(z+1)*2+0], chunkData.textureGrid[x+1][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+0] + ); + //polygonize the current gridcell + polygonize(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); + } + } + } else { + int x = 0; + for(int y = yStartIndex; y < yEndIndex; y++){ + for(int z = zStartIndex; z < zEndIndex; 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), + chunkData.terrainGrid[x+0][y+0][z+0], chunkData.terrainGrid[x+0][y+0][z+1], chunkData.terrainGrid[x+1][y+0][z+1], chunkData.terrainGrid[x+1][y+0][z+0], + chunkData.terrainGrid[x+0][y+1][z+0], chunkData.terrainGrid[x+0][y+1][z+1], chunkData.terrainGrid[x+1][y+1][z+1], chunkData.terrainGrid[x+1][y+1][z+0], + chunkData.textureGrid[x+0][y+0][z+0], chunkData.textureGrid[x+0][y+0][z+1], chunkData.textureGrid[x+1][y+0][z+1], chunkData.textureGrid[x+1][y+0][z+0], + chunkData.textureGrid[x+0][y+1][z+0], chunkData.textureGrid[x+0][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+0] + ); + //polygonize the current gridcell + polygonize(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); + } + } + } + + + + + + + // + //generate the y-positive face + if(chunkData.yPositiveEdgeIso != null){ + int y = ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE - 2; + for(int x = xStartIndex; x < xEndIndex - 1; x++){ + for(int z = zStartIndex; z < zEndIndex - 1; z++){ + // + //Generate the transition cell + // + currentTransitionCell.setValues( + //complex face vertex coordinates + new Vector3f(x,y+1,z), new Vector3f(x+TRANSITION_CELL_WIDTH,y+1,z), new Vector3f(x+1,y+1,z), + new Vector3f(x,y+1,z+TRANSITION_CELL_WIDTH), new Vector3f(x+TRANSITION_CELL_WIDTH,y+1,z+TRANSITION_CELL_WIDTH), new Vector3f(x+1,y+1,z+TRANSITION_CELL_WIDTH), + new Vector3f(x,y+1,z+1), new Vector3f(x+TRANSITION_CELL_WIDTH,y+1,z+1), new Vector3f(x+1,y+1,z+1), + //simple face vertex coordinates + new Vector3f(x,y+TRANSITION_CELL_WIDTH,z), new Vector3f(x+1,y+TRANSITION_CELL_WIDTH,z), + new Vector3f(x,y+TRANSITION_CELL_WIDTH,z+1), new Vector3f(x+1,y+TRANSITION_CELL_WIDTH,z+1), + //complex face iso values + chunkData.yPositiveEdgeIso[(x+0)*2+0][(z+0)*2+0], chunkData.yPositiveEdgeIso[(x+0)*2+1][(z+0)*2+0], chunkData.yPositiveEdgeIso[(x+1)*2+0][(z+0)*2+0], + chunkData.yPositiveEdgeIso[(x+0)*2+0][(z+0)*2+1], chunkData.yPositiveEdgeIso[(x+0)*2+1][(z+0)*2+1], chunkData.yPositiveEdgeIso[(x+1)*2+0][(z+0)*2+1], + chunkData.yPositiveEdgeIso[(x+0)*2+0][(z+1)*2+0], chunkData.yPositiveEdgeIso[(x+0)*2+1][(z+1)*2+0], chunkData.yPositiveEdgeIso[(x+1)*2+0][(z+1)*2+0], + //simple face iso values + chunkData.yPositiveEdgeIso[(x+0)*2+0][(z+0)*2+0], chunkData.yPositiveEdgeIso[(x+1)*2+0][(z+0)*2+0], + chunkData.yPositiveEdgeIso[(x+0)*2+0][(z+1)*2+0], chunkData.yPositiveEdgeIso[(x+1)*2+0][(z+1)*2+0], + //complex face texture atlas values + chunkData.yPositiveEdgeAtlas[(x+0)*2+0][(z+0)*2+0], chunkData.yPositiveEdgeAtlas[(x+0)*2+1][(z+0)*2+0], chunkData.yPositiveEdgeAtlas[(x+1)*2+0][(z+0)*2+0], + chunkData.yPositiveEdgeAtlas[(x+0)*2+0][(z+0)*2+1], chunkData.yPositiveEdgeAtlas[(x+0)*2+1][(z+0)*2+1], chunkData.yPositiveEdgeAtlas[(x+1)*2+0][(z+0)*2+1], + chunkData.yPositiveEdgeAtlas[(x+0)*2+0][(z+1)*2+0], chunkData.yPositiveEdgeAtlas[(x+0)*2+1][(z+1)*2+0], chunkData.yPositiveEdgeAtlas[(x+1)*2+0][(z+1)*2+0], + //simple face texture atlas values + chunkData.yPositiveEdgeAtlas[(x+0)*2+0][(z+0)*2+0], chunkData.yPositiveEdgeAtlas[(x+1)*2+0][(z+0)*2+0], + chunkData.yPositiveEdgeAtlas[(x+0)*2+0][(z+1)*2+0], chunkData.yPositiveEdgeAtlas[(x+1)*2+0][(z+1)*2+0] + ); + polygonizeTransition(currentTransitionCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); + + // + //Generate the normal cell with half width + // + currentCell.setValues( + new Vector3f(x+0,y+0,z+0), new Vector3f(x+0,y+0,z+1), new Vector3f(x+1,y,z+1), new Vector3f(x+1,y,z+0), + new Vector3f(x+0,y+TRANSITION_CELL_WIDTH,z+0), new Vector3f(x+0,y+TRANSITION_CELL_WIDTH,z+1), new Vector3f(x+1,y+TRANSITION_CELL_WIDTH,z+1), new Vector3f(x+1,y+TRANSITION_CELL_WIDTH,z+0), + chunkData.terrainGrid[x+0][y+0][z+0], chunkData.terrainGrid[x+0][y+0][z+1], chunkData.terrainGrid[x+1][y+0][z+1], chunkData.terrainGrid[x+1][y+0][z+0], + chunkData.yPositiveEdgeIso[(x+0)*2+0][(z+0)*2+0], chunkData.yPositiveEdgeIso[(x+0)*2+0][(z+1)*2+0], chunkData.yPositiveEdgeIso[(x+1)*2+0][(z+1)*2+0], chunkData.yPositiveEdgeIso[(x+1)*2+0][(z+0)*2+0], + chunkData.textureGrid[x+0][y+0][z+0], chunkData.textureGrid[x+0][y+0][z+1], chunkData.textureGrid[x+1][y+0][z+1], chunkData.textureGrid[x+1][y+0][z+0], + chunkData.yPositiveEdgeAtlas[(x+0)*2+0][(z+0)*2+0], chunkData.yPositiveEdgeAtlas[(x+0)*2+0][(z+1)*2+0], chunkData.yPositiveEdgeAtlas[(x+1)*2+0][(z+1)*2+0], chunkData.yPositiveEdgeAtlas[(x+1)*2+0][(z+0)*2+0] + ); + //polygonize the current gridcell + polygonize(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); + } + } + } else { + int y = ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE - 2; + for(int x = xStartIndex; x < xEndIndex; x++){ + for(int z = zStartIndex; z < zEndIndex; 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), + chunkData.terrainGrid[x+0][y+0][z+0], chunkData.terrainGrid[x+0][y+0][z+1], chunkData.terrainGrid[x+1][y+0][z+1], chunkData.terrainGrid[x+1][y+0][z+0], + chunkData.terrainGrid[x+0][y+1][z+0], chunkData.terrainGrid[x+0][y+1][z+1], chunkData.terrainGrid[x+1][y+1][z+1], chunkData.terrainGrid[x+1][y+1][z+0], + chunkData.textureGrid[x+0][y+0][z+0], chunkData.textureGrid[x+0][y+0][z+1], chunkData.textureGrid[x+1][y+0][z+1], chunkData.textureGrid[x+1][y+0][z+0], + chunkData.textureGrid[x+0][y+1][z+0], chunkData.textureGrid[x+0][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+0] + ); + //polygonize the current gridcell + polygonize(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); + } + } + } + + + + + + + // + //generate the y-negative face + if(chunkData.yNegativeEdgeIso != null){ + int y = 0; + for(int x = xStartIndex; x < xEndIndex - 1; x++){ + for(int z = zStartIndex; z < zEndIndex - 1; z++){ + // + //Generate the transition cell + // + currentTransitionCell.setValues( + //complex face vertex coordinates + new Vector3f(x,y+0,z), new Vector3f(x+TRANSITION_CELL_WIDTH,y+0,z), new Vector3f(x+1,y+0,z), + new Vector3f(x,y+0,z+TRANSITION_CELL_WIDTH), new Vector3f(x+TRANSITION_CELL_WIDTH,y+0,z+TRANSITION_CELL_WIDTH), new Vector3f(x+1,y+0,z+TRANSITION_CELL_WIDTH), + new Vector3f(x,y+0,z+1), new Vector3f(x+TRANSITION_CELL_WIDTH,y+0,z+1), new Vector3f(x+1,y+0,z+1), + //simple face vertex coordinates + new Vector3f(x,y+TRANSITION_CELL_WIDTH,z), new Vector3f(x+1,y+TRANSITION_CELL_WIDTH,z), + new Vector3f(x,y+TRANSITION_CELL_WIDTH,z+1), new Vector3f(x+1,y+TRANSITION_CELL_WIDTH,z+1), + //complex face iso values + chunkData.yNegativeEdgeIso[(x+0)*2+0][(z+0)*2+0], chunkData.yNegativeEdgeIso[(x+0)*2+1][(z+0)*2+0], chunkData.yNegativeEdgeIso[(x+1)*2+0][(z+0)*2+0], + chunkData.yNegativeEdgeIso[(x+0)*2+0][(z+0)*2+1], chunkData.yNegativeEdgeIso[(x+0)*2+1][(z+0)*2+1], chunkData.yNegativeEdgeIso[(x+1)*2+0][(z+0)*2+1], + chunkData.yNegativeEdgeIso[(x+0)*2+0][(z+1)*2+0], chunkData.yNegativeEdgeIso[(x+0)*2+1][(z+1)*2+0], chunkData.yNegativeEdgeIso[(x+1)*2+0][(z+1)*2+0], + //simple face iso values + chunkData.yNegativeEdgeIso[(x+0)*2+0][(z+0)*2+0], chunkData.yNegativeEdgeIso[(x+1)*2+0][(z+0)*2+0], + chunkData.yNegativeEdgeIso[(x+0)*2+0][(z+1)*2+0], chunkData.yNegativeEdgeIso[(x+1)*2+0][(z+1)*2+0], + //complex face texture atlas values + chunkData.yNegativeEdgeAtlas[(x+0)*2+0][(z+0)*2+0], chunkData.yNegativeEdgeAtlas[(x+0)*2+1][(z+0)*2+0], chunkData.yNegativeEdgeAtlas[(x+1)*2+0][(z+0)*2+0], + chunkData.yNegativeEdgeAtlas[(x+0)*2+0][(z+0)*2+1], chunkData.yNegativeEdgeAtlas[(x+0)*2+1][(z+0)*2+1], chunkData.yNegativeEdgeAtlas[(x+1)*2+0][(z+0)*2+1], + chunkData.yNegativeEdgeAtlas[(x+0)*2+0][(z+1)*2+0], chunkData.yNegativeEdgeAtlas[(x+0)*2+1][(z+1)*2+0], chunkData.yNegativeEdgeAtlas[(x+1)*2+0][(z+1)*2+0], + //simple face texture atlas values + chunkData.yNegativeEdgeAtlas[(x+0)*2+0][(z+0)*2+0], chunkData.yNegativeEdgeAtlas[(x+1)*2+0][(z+0)*2+0], + chunkData.yNegativeEdgeAtlas[(x+0)*2+0][(z+1)*2+0], chunkData.yNegativeEdgeAtlas[(x+1)*2+0][(z+1)*2+0] + ); + polygonizeTransition(currentTransitionCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); + + // + //Generate the normal cell with half width + // + currentCell.setValues( + 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), + new Vector3f(x+0,y+TRANSITION_CELL_WIDTH,z+0), new Vector3f(x+0,y+TRANSITION_CELL_WIDTH,z+1), new Vector3f(x+1,y+TRANSITION_CELL_WIDTH,z+1), new Vector3f(x+1,y+TRANSITION_CELL_WIDTH,z+0), + chunkData.terrainGrid[x+0][y+0][z+0], chunkData.terrainGrid[x+0][y+0][z+1], chunkData.terrainGrid[x+1][y+0][z+1], chunkData.terrainGrid[x+1][y+0][z+0], + chunkData.yNegativeEdgeIso[(x+0)*2+0][(z+0)*2+0], chunkData.yNegativeEdgeIso[(x+0)*2+0][(z+1)*2+0], chunkData.yNegativeEdgeIso[(x+1)*2+0][(z+1)*2+0], chunkData.yNegativeEdgeIso[(x+1)*2+0][(z+0)*2+0], + chunkData.textureGrid[x+0][y+0][z+0], chunkData.textureGrid[x+0][y+0][z+1], chunkData.textureGrid[x+1][y+0][z+1], chunkData.textureGrid[x+1][y+0][z+0], + chunkData.yNegativeEdgeAtlas[(x+0)*2+0][(z+0)*2+0], chunkData.yNegativeEdgeAtlas[(x+0)*2+0][(z+1)*2+0], chunkData.yNegativeEdgeAtlas[(x+1)*2+0][(z+1)*2+0], chunkData.yNegativeEdgeAtlas[(x+1)*2+0][(z+0)*2+0] + ); + //polygonize the current gridcell + polygonize(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); + } + } + } else { + int y = 0; + for(int x = xStartIndex; x < xEndIndex; x++){ + for(int z = zStartIndex; z < zEndIndex; 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), + chunkData.terrainGrid[x+0][y+0][z+0], chunkData.terrainGrid[x+0][y+0][z+1], chunkData.terrainGrid[x+1][y+0][z+1], chunkData.terrainGrid[x+1][y+0][z+0], + chunkData.terrainGrid[x+0][y+1][z+0], chunkData.terrainGrid[x+0][y+1][z+1], chunkData.terrainGrid[x+1][y+1][z+1], chunkData.terrainGrid[x+1][y+1][z+0], + chunkData.textureGrid[x+0][y+0][z+0], chunkData.textureGrid[x+0][y+0][z+1], chunkData.textureGrid[x+1][y+0][z+1], chunkData.textureGrid[x+1][y+0][z+0], + chunkData.textureGrid[x+0][y+1][z+0], chunkData.textureGrid[x+0][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+0] + ); + //polygonize the current gridcell + polygonize(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); + } + } + } + + + + + + + + // + //generate the z-positive face + if(chunkData.zPositiveEdgeIso != null){ + int z = ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE - 2; + for(int x = xStartIndex; x < xEndIndex - 1; x++){ + for(int y = yStartIndex; y < yEndIndex - 1; y++){ + // + //Generate the transition cell + // + currentTransitionCell.setValues( + //complex face vertex coordinates + new Vector3f(x+0,y,z+1), new Vector3f(x+0,y+TRANSITION_CELL_WIDTH,z+1), new Vector3f(x+0,y+1,z+1), + new Vector3f(x+TRANSITION_CELL_WIDTH,y,z+1), new Vector3f(x+TRANSITION_CELL_WIDTH,y+TRANSITION_CELL_WIDTH,z+1), new Vector3f(x+TRANSITION_CELL_WIDTH,y+1,z+1), + new Vector3f(x+1,y,z+1), new Vector3f(x+1,y+TRANSITION_CELL_WIDTH,z+1), new Vector3f(x+1,y+1,z+1), + //simple face vertex coordinates + new Vector3f(x+0,y,z+TRANSITION_CELL_WIDTH), new Vector3f(x+0,y+1,z+TRANSITION_CELL_WIDTH), + new Vector3f(x+1,y,z+TRANSITION_CELL_WIDTH), new Vector3f(x+1,y+1,z+TRANSITION_CELL_WIDTH), + //complex face iso values + chunkData.zPositiveEdgeIso[(x+0)*2+0][(y+0)*2+0], chunkData.zPositiveEdgeIso[(x+0)*2+0][(y+0)*2+1], chunkData.zPositiveEdgeIso[(x+0)*2+0][(y+1)*2+0], + chunkData.zPositiveEdgeIso[(x+0)*2+1][(y+0)*2+0], chunkData.zPositiveEdgeIso[(x+0)*2+1][(y+0)*2+1], chunkData.zPositiveEdgeIso[(x+0)*2+1][(y+1)*2+0], + chunkData.zPositiveEdgeIso[(x+1)*2+0][(y+0)*2+0], chunkData.zPositiveEdgeIso[(x+1)*2+0][(y+0)*2+1], chunkData.zPositiveEdgeIso[(x+1)*2+0][(y+1)*2+0], + //simple face iso values + chunkData.zPositiveEdgeIso[(x+0)*2+0][(y+0)*2+0], chunkData.zPositiveEdgeIso[(x+0)*2+0][(y+1)*2+0], + chunkData.zPositiveEdgeIso[(x+1)*2+0][(y+0)*2+0], chunkData.zPositiveEdgeIso[(x+1)*2+0][(y+1)*2+0], + //complex face texture atlas values + chunkData.zPositiveEdgeAtlas[(x+0)*2+0][(y+0)*2+0], chunkData.zPositiveEdgeAtlas[(x+0)*2+0][(y+0)*2+1], chunkData.zPositiveEdgeAtlas[(x+0)*2+0][(y+1)*2+0], + chunkData.zPositiveEdgeAtlas[(x+0)*2+1][(y+0)*2+0], chunkData.zPositiveEdgeAtlas[(x+0)*2+1][(y+0)*2+1], chunkData.zPositiveEdgeAtlas[(x+0)*2+1][(y+1)*2+0], + chunkData.zPositiveEdgeAtlas[(x+1)*2+0][(y+0)*2+0], chunkData.zPositiveEdgeAtlas[(x+1)*2+0][(y+0)*2+1], chunkData.zPositiveEdgeAtlas[(x+1)*2+0][(y+1)*2+0], + //simple face texture atlas values + chunkData.zPositiveEdgeAtlas[(x+0)*2+0][(y+0)*2+0], chunkData.zPositiveEdgeAtlas[(x+0)*2+0][(y+1)*2+0], + chunkData.zPositiveEdgeAtlas[(x+1)*2+0][(y+0)*2+0], chunkData.zPositiveEdgeAtlas[(x+1)*2+0][(y+1)*2+0] + ); + polygonizeTransition(currentTransitionCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); + + // + //Generate the normal cell with half width + // + currentCell.setValues( + new Vector3f(x+0,y+0,z+0), new Vector3f(x+0,y+0,z+TRANSITION_CELL_WIDTH), new Vector3f(x+1,y+0,z+TRANSITION_CELL_WIDTH), new Vector3f(x+1,y+0,z+0), + new Vector3f(x+0,y+1,z+0), new Vector3f(x+0,y+1,z+TRANSITION_CELL_WIDTH), new Vector3f(x+1,y+1,z+TRANSITION_CELL_WIDTH), new Vector3f(x+1,y+1,z+0), + chunkData.terrainGrid[x+0][y+0][z+0], chunkData.zPositiveEdgeIso[(x+0)*2+0][(y+0)*2+0], chunkData.zPositiveEdgeIso[(x+1)*2+0][(y+0)*2+0], chunkData.terrainGrid[x+1][y+0][z+0], + chunkData.terrainGrid[x+0][y+1][z+0], chunkData.zPositiveEdgeIso[(x+0)*2+0][(y+1)*2+0], chunkData.zPositiveEdgeIso[(x+1)*2+0][(y+1)*2+0], chunkData.terrainGrid[x+1][y+1][z+0], + chunkData.textureGrid[x+0][y+0][z+0], chunkData.zPositiveEdgeAtlas[(x+0)*2+0][(y+0)*2+0], chunkData.zPositiveEdgeAtlas[(x+1)*2+0][(y+0)*2+0], chunkData.textureGrid[x+1][y+0][z+0], + chunkData.textureGrid[x+0][y+1][z+0], chunkData.zPositiveEdgeAtlas[(x+0)*2+0][(y+1)*2+0], chunkData.zPositiveEdgeAtlas[(x+1)*2+0][(y+1)*2+0], chunkData.textureGrid[x+1][y+1][z+0] + ); + //polygonize the current gridcell + polygonize(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); + } + } + } else { + int z = ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE - 2; + for(int x = xStartIndex; x < xEndIndex; x++){ + for(int y = yStartIndex; y < yEndIndex; y++){ + //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), + chunkData.terrainGrid[x+0][y+0][z+0], chunkData.terrainGrid[x+0][y+0][z+1], chunkData.terrainGrid[x+1][y+0][z+1], chunkData.terrainGrid[x+1][y+0][z+0], + chunkData.terrainGrid[x+0][y+1][z+0], chunkData.terrainGrid[x+0][y+1][z+1], chunkData.terrainGrid[x+1][y+1][z+1], chunkData.terrainGrid[x+1][y+1][z+0], + chunkData.textureGrid[x+0][y+0][z+0], chunkData.textureGrid[x+0][y+0][z+1], chunkData.textureGrid[x+1][y+0][z+1], chunkData.textureGrid[x+1][y+0][z+0], + chunkData.textureGrid[x+0][y+1][z+0], chunkData.textureGrid[x+0][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+0] + ); + //polygonize the current gridcell + polygonize(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); + } + } + } + + + + + + + + // + //generate the z-negative face + if(chunkData.zNegativeEdgeIso != null){ + int z = 0; + for(int x = xStartIndex; x < xEndIndex - 1; x++){ + for(int y = yStartIndex; y < yEndIndex - 1; y++){ + // + //Generate the transition cell + // + currentTransitionCell.setValues( + //complex face vertex coordinates + new Vector3f(x+0,y,z), new Vector3f(x+0,y+TRANSITION_CELL_WIDTH,z), new Vector3f(x+0,y+1,z), + new Vector3f(x+TRANSITION_CELL_WIDTH,y,z), new Vector3f(x+TRANSITION_CELL_WIDTH,y+TRANSITION_CELL_WIDTH,z), new Vector3f(x+TRANSITION_CELL_WIDTH,y+1,z), + new Vector3f(x+1,y,z), new Vector3f(x+1,y+TRANSITION_CELL_WIDTH,z), new Vector3f(x+1,y+1,z), + //simple face vertex coordinates + new Vector3f(x+0,y,z+TRANSITION_CELL_WIDTH), new Vector3f(x+0,y+1,z+TRANSITION_CELL_WIDTH), + new Vector3f(x+1,y,z+TRANSITION_CELL_WIDTH), new Vector3f(x+1,y+1,z+TRANSITION_CELL_WIDTH), + //complex face iso values + chunkData.zNegativeEdgeIso[(x+0)*2+0][(y+0)*2+0], chunkData.zNegativeEdgeIso[(x+0)*2+0][(y+0)*2+1], chunkData.zNegativeEdgeIso[(x+0)*2+0][(y+1)*2+0], + chunkData.zNegativeEdgeIso[(x+0)*2+1][(y+0)*2+0], chunkData.zNegativeEdgeIso[(x+0)*2+1][(y+0)*2+1], chunkData.zNegativeEdgeIso[(x+0)*2+1][(y+1)*2+0], + chunkData.zNegativeEdgeIso[(x+1)*2+0][(y+0)*2+0], chunkData.zNegativeEdgeIso[(x+1)*2+0][(y+0)*2+1], chunkData.zNegativeEdgeIso[(x+1)*2+0][(y+1)*2+0], + //simple face iso values + chunkData.zNegativeEdgeIso[(x+0)*2+0][(y+0)*2+0], chunkData.zNegativeEdgeIso[(x+0)*2+0][(y+1)*2+0], + chunkData.zNegativeEdgeIso[(x+1)*2+0][(y+0)*2+0], chunkData.zNegativeEdgeIso[(x+1)*2+0][(y+1)*2+0], + //complex face texture atlas values + chunkData.zNegativeEdgeAtlas[(x+0)*2+0][(y+0)*2+0], chunkData.zNegativeEdgeAtlas[(x+0)*2+0][(y+0)*2+1], chunkData.zNegativeEdgeAtlas[(x+0)*2+0][(y+1)*2+0], + chunkData.zNegativeEdgeAtlas[(x+0)*2+1][(y+0)*2+0], chunkData.zNegativeEdgeAtlas[(x+0)*2+1][(y+0)*2+1], chunkData.zNegativeEdgeAtlas[(x+0)*2+1][(y+1)*2+0], + chunkData.zNegativeEdgeAtlas[(x+1)*2+0][(y+0)*2+0], chunkData.zNegativeEdgeAtlas[(x+1)*2+0][(y+0)*2+1], chunkData.zNegativeEdgeAtlas[(x+1)*2+0][(y+1)*2+0], + //simple face texture atlas values + chunkData.zNegativeEdgeAtlas[(x+0)*2+0][(y+0)*2+0], chunkData.zNegativeEdgeAtlas[(x+0)*2+0][(y+1)*2+0], + chunkData.zNegativeEdgeAtlas[(x+1)*2+0][(y+0)*2+0], chunkData.zNegativeEdgeAtlas[(x+1)*2+0][(y+1)*2+0] + ); + polygonizeTransition(currentTransitionCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); + + // + //Generate the normal cell with half width + // + currentCell.setValues( + new Vector3f(x+0,y+0,z+1), new Vector3f(x+0,y+0,z+TRANSITION_CELL_WIDTH), new Vector3f(x+1,y+0,z+TRANSITION_CELL_WIDTH), new Vector3f(x+1,y+0,z+1), + new Vector3f(x+0,y+1,z+1), new Vector3f(x+0,y+1,z+TRANSITION_CELL_WIDTH), new Vector3f(x+1,y+1,z+TRANSITION_CELL_WIDTH), new Vector3f(x+1,y+1,z+1), + chunkData.terrainGrid[x+0][y+0][z+0], chunkData.zNegativeEdgeIso[(x+0)*2+0][(y+0)*2+0], chunkData.zNegativeEdgeIso[(x+1)*2+0][(y+0)*2+0], chunkData.terrainGrid[x+1][y+0][z+0], + chunkData.terrainGrid[x+0][y+1][z+0], chunkData.zNegativeEdgeIso[(x+0)*2+0][(y+1)*2+0], chunkData.zNegativeEdgeIso[(x+1)*2+0][(y+1)*2+0], chunkData.terrainGrid[x+1][y+1][z+0], + chunkData.textureGrid[x+0][y+0][z+0], chunkData.zNegativeEdgeAtlas[(x+0)*2+0][(y+0)*2+0], chunkData.zNegativeEdgeAtlas[(x+1)*2+0][(y+0)*2+0], chunkData.textureGrid[x+1][y+0][z+0], + chunkData.textureGrid[x+0][y+1][z+0], chunkData.zNegativeEdgeAtlas[(x+0)*2+0][(y+1)*2+0], chunkData.zNegativeEdgeAtlas[(x+1)*2+0][(y+1)*2+0], chunkData.textureGrid[x+1][y+1][z+0] + ); + //polygonize the current gridcell + polygonize(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); + } + } + } else { + int z = 0; + for(int x = xStartIndex; x < xEndIndex; x++){ + for(int y = yStartIndex; y < yEndIndex; y++){ + //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), + chunkData.terrainGrid[x+0][y+0][z+0], chunkData.terrainGrid[x+0][y+0][z+1], chunkData.terrainGrid[x+1][y+0][z+1], chunkData.terrainGrid[x+1][y+0][z+0], + chunkData.terrainGrid[x+0][y+1][z+0], chunkData.terrainGrid[x+0][y+1][z+1], chunkData.terrainGrid[x+1][y+1][z+1], chunkData.terrainGrid[x+1][y+1][z+0], + chunkData.textureGrid[x+0][y+0][z+0], chunkData.textureGrid[x+0][y+0][z+1], chunkData.textureGrid[x+1][y+0][z+1], chunkData.textureGrid[x+1][y+0][z+0], + chunkData.textureGrid[x+0][y+1][z+0], chunkData.textureGrid[x+0][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+0] + ); + //polygonize the current gridcell + polygonize(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); + } + } + } + + + + + //all verts in order, flattened as an array of floats instead of vecs + List vertsFlat = new LinkedList(); + //all normals in order, flattened as an array of floats instead of vecs + List normalsFlat = new LinkedList(); + //all elements of faces in order + List elementsFlat = new LinkedList(); + //List of texture sampler values + List textureSamplers = new LinkedList(); + //List of texture ratio values + List textureRatioData = new LinkedList(); + + float scalingFactor = (float)Math.pow(2,chunkData.levelOfDetail); + + //flatten verts + normals + for(Vector3f vert : verts){ + vertsFlat.add(vert.x * scalingFactor); + vertsFlat.add(vert.y * scalingFactor); + vertsFlat.add(vert.z * scalingFactor); + } + + for(Vector3f normal : normals){ + normalsFlat.add(normal.x); + normalsFlat.add(normal.y); + normalsFlat.add(normal.z); + } + + + for(Triangle triangle : triangles){ + elementsFlat.add(triangle.indices[0]); + elementsFlat.add(triangle.indices[1]); + elementsFlat.add(triangle.indices[2]); + } + + float[] temp = new float[3]; + int i = 0; + for(Vector3f normal : normals){ + Vector3f vert = verts.get(i); + + float absX = Math.abs(normal.x); + float absY = Math.abs(normal.y); + float absZ = Math.abs(normal.z); + + float uvX = vert.z * absX + vert.x * absY + vert.x * absZ; + float uvY = vert.y * absX + vert.z * absY + vert.y * absZ; + temp[0] = uvX; + temp[1] = uvY; + + // if(absX >= absZ && absX >= absY){ + // temp[0] = normal.z / 2.0f + 0.5f + vert.z * (absX / (absX + absZ)) + vert.x * (absZ / (absX + absZ)); + // temp[1] = normal.y / 2.0f + 0.5f + vert.x * (absY / (absX + absY)) + vert.y * (absX / (absX + absY)); + // } else if(absZ >= absX && absZ >= absY){ + // temp[0] = normal.x / 2.0f + 0.5f + vert.z * (absX / (absX + absZ)) + vert.x * (absZ / (absX + absZ)); + // temp[1] = normal.y / 2.0f + 0.5f + vert.z * (absY / (absZ + absY)) + vert.y * (absZ / (absZ + absY)); + // } else if(absY >= absX && absY >= absZ){ + // temp[0] = normal.x / 2.0f + 0.5f + vert.y * (absX / (absX + absY)) + vert.x * (absY / (absX + absY)); + // temp[1] = normal.z / 2.0f + 0.5f + vert.y * (absZ / (absZ + absY)) + vert.z * (absY / (absZ + absY)); + // } else { + // temp[0] = vert.x / 1.5f + vert.z / 1.5f; + // temp[1] = vert.y / 1.5f + vert.z / 1.5f; + // } + i++; + UVs.add(temp[0]); + UVs.add(temp[1]); + } + + //flatten sampler indices + for(Vector3f samplerVec : samplerTriangles){ + textureSamplers.add((float)Globals.voxelTextureAtlas.getVoxelTypeOffset((int)samplerVec.x)); + textureSamplers.add((float)Globals.voxelTextureAtlas.getVoxelTypeOffset((int)samplerVec.y)); + textureSamplers.add((float)Globals.voxelTextureAtlas.getVoxelTypeOffset((int)samplerVec.z)); + } + + //set ratio dat + for(int j = 0; j < triangles.size(); j++){ + //first vertex + textureRatioData.add(1.0f); + textureRatioData.add(0.0f); + textureRatioData.add(0.0f); + + //second vertex + textureRatioData.add(0.0f); + textureRatioData.add(1.0f); + textureRatioData.add(0.0f); + + //third vertex + textureRatioData.add(0.0f); + textureRatioData.add(0.0f); + textureRatioData.add(1.0f); + } + + //List vertices, List normals, List faceElements, List uvs + TerrainChunkData rVal = new TerrainChunkData(vertsFlat, normalsFlat, elementsFlat, UVs, textureSamplers, textureRatioData); + return rVal; + } + + + + + + + + + /** + * Contains data required to generate a transvoxel mesh + */ + public static class TransvoxelChunkData { + + /** + + 5 6 + +-------------+ +-----5-------+ ^ Y + / | / | / | /| | _ + / | / | 4 9 6 10 | /\ Z + 4 +-----+-------+ 7 | +-----+7------+ | | / + | 1 +-------+-----+ 2 | +-----1-+-----+ | / + | / | / 8 0 11 2 | / + | / | / | / | / |/ + 0 +-------------+ 3 +------3------+ +---------------> X + + + */ + + /** + The assumption is that the edge arrays are laid out perfectly adjacent + ie + + + +---------------+---------------+ + | | | + | | | + | higher res | higher res | + | chunk 1 | chunk 2 | + | | | + | | | + | | | + +---------------+---------------+ + | | | + | | | + | higher res | higher res | + | chunk 3 | chunk 4 | + | | | + | | | + | | | + +---------------+---------------+ + + + */ + + float[][] xPositiveEdgeIso = null; //full-resolution values for the x-positive edge if it is twice the resolution of the core mesh + int[][] xPositiveEdgeAtlas = null; + float[][] xNegativeEdgeIso = null; //full-resolution values for the x-negative edge if it is twice the resolution of the core mesh + int[][] xNegativeEdgeAtlas = null; + float[][] yPositiveEdgeIso = null; //full-resolution values for the y-positive edge if it is twice the resolution of the core mesh + int[][] yPositiveEdgeAtlas = null; + float[][] yNegativeEdgeIso = null; //full-resolution values for the y-negative edge if it is twice the resolution of the core mesh + int[][] yNegativeEdgeAtlas = null; + float[][] zPositiveEdgeIso = null; //full-resolution values for the z-positive edge if it is twice the resolution of the core mesh + int[][] zPositiveEdgeAtlas = null; + float[][] zNegativeEdgeIso = null; //full-resolution values for the z-negative edge if it is twice the resolution of the core mesh + int[][] zNegativeEdgeAtlas = null; + + //the core voxel data for the main part of the mesh + public float[][][] terrainGrid; + + //the core texture data for the main part of the mesh + public int[][][] textureGrid; + + int levelOfDetail; + + /** + * Constructor -- please note that the edge arrays do not need to be defined + * They are left as null if there is not a higher resolution edge to this chunk + */ + public TransvoxelChunkData( + float[][][] terrainGrid, + int[][][] textureGrid, + int levelOfDetail + ){ + this.terrainGrid = terrainGrid; + this.textureGrid = textureGrid; + this.levelOfDetail = levelOfDetail; + } + + + /** + * Adds values for the face along positive x + * @param isoValues the iso values + * @param atlasValues the atlas values + */ + public void addXPositiveEdge(float[][] isoValues, int[][] atlasValues){ + this.xPositiveEdgeIso = isoValues; + this.xPositiveEdgeAtlas = atlasValues; + } + + /** + * Adds values for the face along negative x + * @param isoValues the iso values + * @param atlasValues the atlas values + */ + public void addXNegativeEdge(float[][] isoValues, int[][] atlasValues){ + this.xNegativeEdgeIso = isoValues; + this.xNegativeEdgeAtlas = atlasValues; + } + + /** + * Adds values for the face along positive y + * @param isoValues the iso values + * @param atlasValues the atlas values + */ + public void addYPositiveEdge(float[][] isoValues, int[][] atlasValues){ + this.yPositiveEdgeIso = isoValues; + this.yPositiveEdgeAtlas = atlasValues; + } + + /** + * Adds values for the face along negative y + * @param isoValues the iso values + * @param atlasValues the atlas values + */ + public void addYNegativeEdge(float[][] isoValues, int[][] atlasValues){ + this.yNegativeEdgeIso = isoValues; + this.yNegativeEdgeAtlas = atlasValues; + } + + /** + * Adds values for the face along positive z + * @param isoValues the iso values + * @param atlasValues the atlas values + */ + public void addZPositiveEdge(float[][] isoValues, int[][] atlasValues){ + this.zPositiveEdgeIso = isoValues; + this.zPositiveEdgeAtlas = atlasValues; + } + + /** + * Adds values for the face along negative z + * @param isoValues the iso values + * @param atlasValues the atlas values + */ + public void addZNegativeEdge(float[][] isoValues, int[][] atlasValues){ + this.zNegativeEdgeIso = isoValues; + this.zNegativeEdgeAtlas = atlasValues; + } + + + + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + //The lookup table that maps iso value to the index to lookup for texture + static final int[][] sampleIndexTable = new int[][]{ + {0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3}, + {1, 2, 3, 0, 5, 6, 7, 4, 4, 5, 6, 7}, + }; + + + + + + + + + + + + // The transitionCellClass table maps a 9-bit transition cell case index to an equivalence + // class index. Even though there are 73 equivalence classes in the Transvoxel Algorithm, + // several of them use the same exact triangulations, just with different vertex locations. + // We combined those classes for this table so that the class index ranges from 0 to 55. + // The high bit is set in the cases for which the inverse state of the voxel data maps to + // the equivalence class, meaning that the winding order of each triangle should be reversed. + static final short transitionCellClass[] = new short[]{ + 0x00, 0x01, 0x02, 0x84, 0x01, 0x05, 0x04, 0x04, 0x02, 0x87, 0x09, 0x8C, 0x84, 0x0B, 0x05, 0x05, + 0x01, 0x08, 0x07, 0x8D, 0x05, 0x0F, 0x8B, 0x0B, 0x04, 0x0D, 0x0C, 0x1C, 0x04, 0x8B, 0x85, 0x85, + 0x02, 0x07, 0x09, 0x8C, 0x87, 0x10, 0x0C, 0x0C, 0x09, 0x12, 0x15, 0x9A, 0x8C, 0x19, 0x90, 0x10, + 0x84, 0x8D, 0x8C, 0x9C, 0x0B, 0x9D, 0x0F, 0x0F, 0x05, 0x1B, 0x10, 0xAC, 0x05, 0x0F, 0x8B, 0x0B, + 0x01, 0x05, 0x87, 0x0B, 0x08, 0x0F, 0x0D, 0x8B, 0x07, 0x10, 0x12, 0x19, 0x8D, 0x9D, 0x1B, 0x0F, + 0x05, 0x0F, 0x10, 0x9D, 0x0F, 0x1E, 0x1D, 0xA1, 0x8B, 0x1D, 0x99, 0x32, 0x0B, 0xA1, 0x8F, 0x94, + 0x04, 0x8B, 0x0C, 0x0F, 0x0D, 0x1D, 0x1C, 0x8F, 0x0C, 0x99, 0x1A, 0x31, 0x1C, 0x32, 0x2C, 0xA7, + 0x04, 0x0B, 0x0C, 0x0F, 0x8B, 0xA1, 0x8F, 0x96, 0x85, 0x8F, 0x90, 0x27, 0x85, 0x94, 0x8B, 0x8A, + 0x02, 0x04, 0x09, 0x05, 0x07, 0x8B, 0x0C, 0x85, 0x09, 0x0C, 0x15, 0x90, 0x8C, 0x0F, 0x10, 0x8B, + 0x87, 0x0D, 0x12, 0x1B, 0x10, 0x1D, 0x99, 0x8F, 0x0C, 0x1C, 0x1A, 0x2C, 0x0C, 0x8F, 0x90, 0x8B, + 0x09, 0x0C, 0x15, 0x10, 0x12, 0x99, 0x1A, 0x90, 0x15, 0x1A, 0x23, 0x30, 0x9A, 0x31, 0x30, 0x19, + 0x8C, 0x1C, 0x9A, 0xAC, 0x19, 0x32, 0x31, 0x27, 0x90, 0x2C, 0x30, 0x29, 0x10, 0xA7, 0x19, 0x24, + 0x84, 0x04, 0x8C, 0x05, 0x8D, 0x0B, 0x1C, 0x85, 0x8C, 0x0C, 0x9A, 0x10, 0x9C, 0x0F, 0xAC, 0x0B, + 0x0B, 0x8B, 0x19, 0x0F, 0x9D, 0xA1, 0x32, 0x94, 0x0F, 0x8F, 0x31, 0xA7, 0x0F, 0x96, 0x27, 0x8A, + 0x05, 0x85, 0x90, 0x8B, 0x1B, 0x8F, 0x2C, 0x8B, 0x10, 0x90, 0x30, 0x19, 0xAC, 0x27, 0x29, 0x24, + 0x05, 0x85, 0x10, 0x0B, 0x0F, 0x94, 0xA7, 0x8A, 0x8B, 0x8B, 0x19, 0x24, 0x0B, 0x8A, 0x24, 0x83, + 0x03, 0x06, 0x0A, 0x8B, 0x06, 0x0E, 0x0B, 0x0B, 0x0A, 0x91, 0x14, 0x8F, 0x8B, 0x17, 0x05, 0x85, + 0x06, 0x13, 0x11, 0x98, 0x0E, 0x1F, 0x97, 0x2B, 0x0B, 0x18, 0x0F, 0x36, 0x0B, 0xAB, 0x05, 0x85, + 0x0A, 0x11, 0x16, 0x8F, 0x91, 0x20, 0x0F, 0x8F, 0x14, 0x22, 0x21, 0x1D, 0x8F, 0x2D, 0x0B, 0x8B, + 0x8B, 0x98, 0x8F, 0xB7, 0x17, 0xAE, 0x8C, 0x0C, 0x05, 0x2F, 0x8B, 0xB5, 0x85, 0xA6, 0x84, 0x04, + 0x06, 0x0E, 0x91, 0x17, 0x13, 0x1F, 0x18, 0xAB, 0x11, 0x20, 0x22, 0x2D, 0x98, 0xAE, 0x2F, 0xA6, + 0x0E, 0x1F, 0x20, 0xAE, 0x1F, 0x33, 0x2E, 0x2A, 0x97, 0x2E, 0xAD, 0x28, 0x2B, 0x2A, 0x26, 0x25, + 0x0B, 0x97, 0x0F, 0x8C, 0x18, 0x2E, 0x37, 0x8C, 0x0F, 0xAD, 0x9D, 0x90, 0x36, 0x28, 0x35, 0x07, + 0x0B, 0x2B, 0x8F, 0x0C, 0xAB, 0x2A, 0x8C, 0x89, 0x05, 0x26, 0x0B, 0x87, 0x85, 0x25, 0x84, 0x82, + 0x0A, 0x0B, 0x14, 0x05, 0x11, 0x97, 0x0F, 0x05, 0x16, 0x0F, 0x21, 0x0B, 0x8F, 0x8C, 0x8B, 0x84, + 0x91, 0x18, 0x22, 0x2F, 0x20, 0x2E, 0xAD, 0x26, 0x0F, 0x37, 0x9D, 0x35, 0x8F, 0x8C, 0x0B, 0x84, + 0x14, 0x0F, 0x21, 0x8B, 0x22, 0xAD, 0x9D, 0x0B, 0x21, 0x9D, 0x9E, 0x8F, 0x1D, 0x90, 0x8F, 0x85, + 0x8F, 0x36, 0x1D, 0xB5, 0x2D, 0x28, 0x90, 0x87, 0x0B, 0x35, 0x8F, 0x34, 0x8B, 0x07, 0x85, 0x81, + 0x8B, 0x0B, 0x8F, 0x85, 0x98, 0x2B, 0x36, 0x85, 0x8F, 0x8F, 0x1D, 0x8B, 0xB7, 0x0C, 0xB5, 0x04, + 0x17, 0xAB, 0x2D, 0xA6, 0xAE, 0x2A, 0x28, 0x25, 0x8C, 0x8C, 0x90, 0x07, 0x0C, 0x89, 0x87, 0x82, + 0x05, 0x05, 0x0B, 0x84, 0x2F, 0x26, 0x35, 0x84, 0x8B, 0x0B, 0x8F, 0x85, 0xB5, 0x87, 0x34, 0x81, + 0x85, 0x85, 0x8B, 0x04, 0xA6, 0x25, 0x07, 0x82, 0x84, 0x84, 0x85, 0x81, 0x04, 0x82, 0x81, 0x80 + }; + + + // The transitionCellData table holds the triangulation data for all 56 distinct classes to + // which a case can be mapped by the transitionCellClass table. The class index should be ANDed + // with 0x7F before using it to look up triangulation data in this table. + static final TransitionCellData[] transitionCellData = new TransitionCellData[]{ + new TransitionCellData((char)0x00, new char[]{}), + new TransitionCellData((char)0x42, new char[]{0, 1, 3, 1, 2, 3}), + new TransitionCellData((char)0x31, new char[]{0, 1, 2}), + new TransitionCellData((char)0x42, new char[]{0, 1, 2, 0, 2, 3}), + new TransitionCellData((char)0x53, new char[]{0, 1, 4, 1, 3, 4, 1, 2, 3}), + new TransitionCellData((char)0x64, new char[]{0, 1, 5, 1, 2, 5, 2, 4, 5, 2, 3, 4}), + new TransitionCellData((char)0x84, new char[]{0, 1, 3, 1, 2, 3, 4, 5, 6, 4, 6, 7}), + new TransitionCellData((char)0x73, new char[]{0, 1, 3, 1, 2, 3, 4, 5, 6}), + new TransitionCellData((char)0x84, new char[]{0, 1, 3, 1, 2, 3, 4, 5, 7, 5, 6, 7}), + new TransitionCellData((char)0x62, new char[]{0, 1, 2, 3, 4, 5}), + new TransitionCellData((char)0x53, new char[]{0, 1, 3, 0, 3, 4, 1, 2, 3}), + new TransitionCellData((char)0x75, new char[]{0, 1, 6, 1, 2, 6, 2, 5, 6, 2, 3, 5, 3, 4, 5}), + new TransitionCellData((char)0x84, new char[]{0, 1, 4, 1, 3, 4, 1, 2, 3, 5, 6, 7}), + new TransitionCellData((char)0x95, new char[]{0, 1, 4, 1, 3, 4, 1, 2, 3, 5, 6, 8, 6, 7, 8}), + new TransitionCellData((char)0xA6, new char[]{0, 1, 5, 1, 2, 5, 2, 4, 5, 2, 3, 4, 6, 7, 8, 6, 8, 9}), + new TransitionCellData((char)0x86, new char[]{0, 1, 7, 1, 2, 7, 2, 3, 7, 3, 6, 7, 3, 4, 6, 4, 5, 6}), + new TransitionCellData((char)0x95, new char[]{0, 1, 5, 1, 2, 5, 2, 4, 5, 2, 3, 4, 6, 7, 8}), + new TransitionCellData((char)0x95, new char[]{0, 1, 3, 1, 2, 3, 4, 5, 7, 4, 7, 8, 5, 6, 7}), + new TransitionCellData((char)0xA4, new char[]{0, 1, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9}), + new TransitionCellData((char)0xC6, new char[]{0, 1, 3, 1, 2, 3, 4, 5, 7, 5, 6, 7, 8, 9, 10, 8, 10, 11}), + new TransitionCellData((char)0x64, new char[]{0, 1, 3, 1, 2, 3, 0, 3, 4, 0, 4, 5}), + new TransitionCellData((char)0x93, new char[]{0, 1, 2, 3, 4, 5, 6, 7, 8}), + new TransitionCellData((char)0x64, new char[]{0, 1, 4, 0, 4, 5, 1, 3, 4, 1, 2, 3}), + new TransitionCellData((char)0x97, new char[]{0, 1, 8, 1, 7, 8, 1, 2, 7, 2, 3, 7, 3, 4, 7, 4, 5, 7, 5, 6, 7}), + new TransitionCellData((char)0xB7, new char[]{0, 1, 6, 1, 2, 6, 2, 5, 6, 2, 3, 5, 3, 4, 5, 7, 8, 10, 8, 9, 10}), + new TransitionCellData((char)0xA6, new char[]{0, 1, 6, 1, 2, 6, 2, 5, 6, 2, 3, 5, 3, 4, 5, 7, 8, 9}), + new TransitionCellData((char)0xB5, new char[]{0, 1, 4, 1, 3, 4, 1, 2, 3, 5, 6, 7, 8, 9, 10}), + new TransitionCellData((char)0xA6, new char[]{0, 1, 5, 1, 2, 5, 2, 4, 5, 2, 3, 4, 6, 7, 9, 7, 8, 9}), + new TransitionCellData((char)0xA6, new char[]{0, 1, 4, 1, 3, 4, 1, 2, 3, 5, 6, 9, 6, 8, 9, 6, 7, 8}), + new TransitionCellData((char)0x97, new char[]{0, 1, 8, 1, 2, 8, 2, 3, 8, 3, 7, 8, 3, 4, 7, 4, 5, 7, 5, 6, 7}), + new TransitionCellData((char)0x86, new char[]{0, 1, 7, 1, 6, 7, 1, 2, 6, 2, 5, 6, 2, 4, 5, 2, 3, 4}), + new TransitionCellData((char)0xC8, new char[]{0, 1, 7, 1, 2, 7, 2, 3, 7, 3, 6, 7, 3, 4, 6, 4, 5, 6, 8, 9, 10, 8, 10, 11}), + new TransitionCellData((char)0xB7, new char[]{0, 1, 5, 1, 2, 5, 2, 4, 5, 2, 3, 4, 6, 9, 10, 6, 7, 9, 7, 8, 9}), + new TransitionCellData((char)0x75, new char[]{0, 1, 6, 1, 3, 6, 1, 2, 3, 3, 4, 6, 4, 5, 6}), + new TransitionCellData((char)0xA6, new char[]{0, 1, 3, 1, 2, 3, 4, 5, 9, 5, 8, 9, 5, 6, 8, 6, 7, 8}), + new TransitionCellData((char)0xC4, new char[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), + new TransitionCellData((char)0x86, new char[]{1, 2, 4, 2, 3, 4, 0, 1, 7, 1, 4, 7, 4, 6, 7, 4, 5, 6}), + new TransitionCellData((char)0x64, new char[]{0, 4, 5, 0, 1, 4, 1, 3, 4, 1, 2, 3}), + new TransitionCellData((char)0x86, new char[]{0, 1, 4, 1, 3, 4, 1, 2, 3, 0, 4, 7, 4, 6, 7, 4, 5, 6}), + new TransitionCellData((char)0x97, new char[]{1, 2, 3, 1, 3, 4, 1, 4, 5, 0, 1, 8, 1, 5, 8, 5, 7, 8, 5, 6, 7}), + new TransitionCellData((char)0xA6, new char[]{0, 1, 3, 1, 2, 3, 4, 5, 9, 5, 8, 9, 5, 6, 8, 6, 7, 8}), + new TransitionCellData((char)0xC8, new char[]{0, 1, 5, 1, 2, 5, 2, 4, 5, 2, 3, 4, 6, 7, 11, 7, 10, 11, 7, 8, 10, 8, 9, 10}), + new TransitionCellData((char)0x97, new char[]{0, 1, 8, 1, 2, 8, 2, 7, 8, 2, 3, 7, 3, 6, 7, 3, 4, 6, 4, 5, 6}), + new TransitionCellData((char)0x97, new char[]{0, 1, 4, 1, 3, 4, 1, 2, 3, 0, 4, 8, 4, 7, 8, 4, 5, 7, 5, 6, 7}), + new TransitionCellData((char)0xB7, new char[]{0, 1, 5, 1, 2, 5, 2, 4, 5, 2, 3, 4, 6, 7, 10, 7, 9, 10, 7, 8, 9}), + new TransitionCellData((char)0xA8, new char[]{0, 1, 9, 1, 2, 9, 2, 8, 9, 2, 3, 8, 3, 7, 8, 3, 4, 7, 4, 6, 7, 4, 5, 6}), + new TransitionCellData((char)0xB9, new char[]{0, 1, 7, 1, 6, 7, 1, 2, 6, 2, 5, 6, 2, 3, 5, 3, 4, 5, 0, 7, 10, 7, 9, 10, 7, 8, 9}), + new TransitionCellData((char)0xA6, new char[]{0, 1, 5, 1, 4, 5, 1, 2, 4, 2, 3, 4, 6, 7, 9, 7, 8, 9}), + new TransitionCellData((char)0xC6, new char[]{0, 1, 5, 1, 2, 5, 2, 4, 5, 2, 3, 4, 6, 7, 8, 9, 10, 11}), + new TransitionCellData((char)0xB7, new char[]{0, 1, 7, 1, 2, 7, 2, 3, 7, 3, 6, 7, 3, 4, 6, 4, 5, 6, 8, 9, 10}), + new TransitionCellData((char)0xA8, new char[]{1, 2, 3, 1, 3, 4, 1, 4, 6, 4, 5, 6, 0, 1, 9, 1, 6, 9, 6, 8, 9, 6, 7, 8}), + new TransitionCellData((char)0xCC, new char[]{0, 1, 9, 1, 8, 9, 1, 2, 8, 2, 11, 8, 2, 3, 11, 3, 4, 11, 4, 5, 11, 5, 10, 11, 5, 6, 10, 6, 9, 10, 6, 7, 9, 7, 0, 9}), + new TransitionCellData((char)0x86, new char[]{0, 1, 2, 0, 2, 3, 0, 6, 7, 0, 3, 6, 1, 4, 5, 1, 5, 2}), + new TransitionCellData((char)0x97, new char[]{0, 1, 4, 1, 3, 4, 1, 2, 3, 2, 5, 6, 2, 6, 3, 0, 7, 8, 0, 4, 7}), + new TransitionCellData((char)0xA8, new char[]{0, 1, 5, 1, 4, 5, 1, 2, 4, 2, 3, 4, 3, 6, 7, 3, 7, 4, 0, 8, 9, 0, 5, 8}), + new TransitionCellData((char)0xA8, new char[]{0, 1, 5, 1, 4, 5, 1, 2, 4, 2, 3, 4, 2, 6, 3, 3, 6, 7, 0, 8, 9, 0, 5, 8}), + }; + + // The transitionCornerData table contains the transition cell corner reuse data + // shown in Figure 4.19 + static final char[] transitionCornerData = new char[]{ + 0x30, 0x21, 0x20, 0x12, 0x40, 0x82, 0x10, 0x81, 0x80, 0x37, 0x27, 0x17, 0x87 + }; + + + // The transitionVertexData table gives the vertex locations for every one of the 512 possible + // cases in the Tranvoxel Algorithm. Each 16-bit value also provides information about whether + // a vertex can be reused from a neighboring cell. See Section 4.5 for details. The low byte + // contains the indexes for the two endpoints of the edge on which the vertex lies, as numbered + // in Figure 4.16. The high byte contains the vertex reuse data shown in Figure 4.17. + static final int transitionVertexData[][] = new int[][]{ + {}, + {0x2301, 0x1503, 0x199B, 0x289A}, + {0x2301, 0x2412, 0x4514}, + {0x1503, 0x4514, 0x2412, 0x289A, 0x199B}, + {0x8525, 0x2412, 0x289A, 0x89AC}, + {0x8525, 0x2412, 0x2301, 0x1503, 0x199B, 0x89AC}, + {0x8525, 0x4514, 0x2301, 0x289A, 0x89AC}, + {0x8525, 0x4514, 0x1503, 0x199B, 0x89AC}, + {0x8525, 0x8658, 0x4445}, + {0x1503, 0x2301, 0x289A, 0x199B, 0x8658, 0x8525, 0x4445}, + {0x8525, 0x8658, 0x4445, 0x2301, 0x2412, 0x4514}, + {0x1503, 0x4514, 0x2412, 0x289A, 0x199B, 0x8658, 0x8525, 0x4445}, + {0x2412, 0x4445, 0x8658, 0x89AC, 0x289A}, + {0x8658, 0x4445, 0x2412, 0x2301, 0x1503, 0x199B, 0x89AC}, + {0x8658, 0x4445, 0x4514, 0x2301, 0x289A, 0x89AC}, + {0x8658, 0x4445, 0x4514, 0x1503, 0x199B, 0x89AC}, + {0x8478, 0x8658, 0x89AC, 0x88BC}, + {0x2301, 0x1503, 0x199B, 0x289A, 0x8478, 0x8658, 0x89AC, 0x88BC}, + {0x8478, 0x8658, 0x89AC, 0x88BC, 0x2301, 0x2412, 0x4514}, + {0x1503, 0x4514, 0x2412, 0x289A, 0x199B, 0x8658, 0x8478, 0x88BC, 0x89AC}, + {0x8478, 0x8658, 0x8525, 0x2412, 0x289A, 0x88BC}, + {0x8478, 0x8658, 0x8525, 0x2412, 0x2301, 0x1503, 0x199B, 0x88BC}, + {0x2301, 0x4514, 0x8525, 0x8658, 0x8478, 0x88BC, 0x289A}, + {0x8478, 0x8658, 0x8525, 0x4514, 0x1503, 0x199B, 0x88BC}, + {0x8478, 0x4445, 0x8525, 0x89AC, 0x88BC}, + {0x8478, 0x4445, 0x8525, 0x89AC, 0x88BC, 0x2301, 0x1503, 0x199B, 0x289A}, + {0x8478, 0x4445, 0x8525, 0x89AC, 0x88BC, 0x2301, 0x2412, 0x4514}, + {0x8478, 0x4445, 0x8525, 0x89AC, 0x88BC, 0x2412, 0x4514, 0x1503, 0x199B, 0x289A}, + {0x8478, 0x4445, 0x2412, 0x289A, 0x88BC}, + {0x1503, 0x2301, 0x2412, 0x4445, 0x8478, 0x88BC, 0x199B}, + {0x2301, 0x4514, 0x4445, 0x8478, 0x88BC, 0x289A}, + {0x1503, 0x4514, 0x4445, 0x8478, 0x88BC, 0x199B}, + {0x8478, 0x8367, 0x4647}, + {0x2301, 0x1503, 0x199B, 0x289A, 0x8478, 0x8367, 0x4647}, + {0x2301, 0x2412, 0x4514, 0x8478, 0x8367, 0x4647}, + {0x1503, 0x4514, 0x2412, 0x289A, 0x199B, 0x8367, 0x8478, 0x4647}, + {0x2412, 0x8525, 0x89AC, 0x289A, 0x8367, 0x8478, 0x4647}, + {0x8525, 0x2412, 0x2301, 0x1503, 0x199B, 0x89AC, 0x8478, 0x8367, 0x4647}, + {0x8525, 0x4514, 0x2301, 0x289A, 0x89AC, 0x8478, 0x8367, 0x4647}, + {0x8525, 0x4514, 0x1503, 0x199B, 0x89AC, 0x8478, 0x8367, 0x4647}, + {0x8478, 0x8367, 0x4647, 0x8525, 0x8658, 0x4445}, + {0x2301, 0x1503, 0x199B, 0x289A, 0x8478, 0x8367, 0x4647, 0x8525, 0x8658, 0x4445}, + {0x8478, 0x8367, 0x4647, 0x8525, 0x8658, 0x4445, 0x2301, 0x2412, 0x4514}, + {0x1503, 0x4514, 0x2412, 0x289A, 0x199B, 0x8658, 0x8525, 0x4445, 0x8367, 0x8478, 0x4647}, + {0x2412, 0x4445, 0x8658, 0x89AC, 0x289A, 0x8367, 0x8478, 0x4647}, + {0x8658, 0x4445, 0x2412, 0x2301, 0x1503, 0x199B, 0x89AC, 0x8478, 0x8367, 0x4647}, + {0x2301, 0x4514, 0x4445, 0x8658, 0x89AC, 0x289A, 0x8367, 0x8478, 0x4647}, + {0x8658, 0x4445, 0x4514, 0x1503, 0x199B, 0x89AC, 0x8478, 0x8367, 0x4647}, + {0x8658, 0x4647, 0x8367, 0x88BC, 0x89AC}, + {0x8658, 0x4647, 0x8367, 0x88BC, 0x89AC, 0x1503, 0x2301, 0x289A, 0x199B}, + {0x8658, 0x4647, 0x8367, 0x88BC, 0x89AC, 0x2412, 0x2301, 0x4514}, + {0x1503, 0x4514, 0x2412, 0x289A, 0x199B, 0x8658, 0x4647, 0x8367, 0x88BC, 0x89AC}, + {0x8367, 0x4647, 0x8658, 0x8525, 0x2412, 0x289A, 0x88BC}, + {0x1503, 0x2301, 0x2412, 0x8525, 0x8658, 0x4647, 0x8367, 0x88BC, 0x199B}, + {0x8367, 0x4647, 0x8658, 0x8525, 0x4514, 0x2301, 0x289A, 0x88BC}, + {0x8367, 0x4647, 0x8658, 0x8525, 0x4514, 0x1503, 0x199B, 0x88BC}, + {0x8367, 0x4647, 0x4445, 0x8525, 0x89AC, 0x88BC}, + {0x8367, 0x4647, 0x4445, 0x8525, 0x89AC, 0x88BC, 0x2301, 0x1503, 0x199B, 0x289A}, + {0x8367, 0x4647, 0x4445, 0x8525, 0x89AC, 0x88BC, 0x2301, 0x2412, 0x4514}, + {0x8525, 0x4445, 0x4647, 0x8367, 0x88BC, 0x89AC, 0x1503, 0x4514, 0x2412, 0x289A, 0x199B}, + {0x8367, 0x4647, 0x4445, 0x2412, 0x289A, 0x88BC}, + {0x8367, 0x4647, 0x4445, 0x2412, 0x2301, 0x1503, 0x199B, 0x88BC}, + {0x2301, 0x4514, 0x4445, 0x4647, 0x8367, 0x88BC, 0x289A}, + {0x8367, 0x4647, 0x4445, 0x4514, 0x1503, 0x199B, 0x88BC}, + {0x1636, 0x8367, 0x88BC, 0x199B}, + {0x2301, 0x1503, 0x1636, 0x8367, 0x88BC, 0x289A}, + {0x8367, 0x1636, 0x199B, 0x88BC, 0x2412, 0x2301, 0x4514}, + {0x2412, 0x4514, 0x1503, 0x1636, 0x8367, 0x88BC, 0x289A}, + {0x8525, 0x2412, 0x289A, 0x89AC, 0x1636, 0x8367, 0x88BC, 0x199B}, + {0x8525, 0x2412, 0x2301, 0x1503, 0x1636, 0x8367, 0x88BC, 0x89AC}, + {0x8525, 0x4514, 0x2301, 0x289A, 0x89AC, 0x1636, 0x8367, 0x88BC, 0x199B}, + {0x8367, 0x1636, 0x1503, 0x4514, 0x8525, 0x89AC, 0x88BC}, + {0x1636, 0x8367, 0x88BC, 0x199B, 0x8525, 0x8658, 0x4445}, + {0x2301, 0x1503, 0x1636, 0x8367, 0x88BC, 0x289A, 0x8525, 0x8658, 0x4445}, + {0x1636, 0x8367, 0x88BC, 0x199B, 0x8525, 0x8658, 0x4445, 0x2301, 0x2412, 0x4514}, + {0x2412, 0x4514, 0x1503, 0x1636, 0x8367, 0x88BC, 0x289A, 0x8525, 0x8658, 0x4445}, + {0x2412, 0x4445, 0x8658, 0x89AC, 0x289A, 0x8367, 0x1636, 0x199B, 0x88BC}, + {0x8367, 0x1636, 0x1503, 0x2301, 0x2412, 0x4445, 0x8658, 0x89AC, 0x88BC}, + {0x8658, 0x4445, 0x4514, 0x2301, 0x289A, 0x89AC, 0x1636, 0x8367, 0x88BC, 0x199B}, + {0x8658, 0x4445, 0x4514, 0x1503, 0x1636, 0x8367, 0x88BC, 0x89AC}, + {0x1636, 0x8367, 0x8478, 0x8658, 0x89AC, 0x199B}, + {0x2301, 0x1503, 0x1636, 0x8367, 0x8478, 0x8658, 0x89AC, 0x289A}, + {0x1636, 0x8367, 0x8478, 0x8658, 0x89AC, 0x199B, 0x2301, 0x2412, 0x4514}, + {0x8658, 0x8478, 0x8367, 0x1636, 0x1503, 0x4514, 0x2412, 0x289A, 0x89AC}, + {0x1636, 0x8367, 0x8478, 0x8658, 0x8525, 0x2412, 0x289A, 0x199B}, + {0x1636, 0x8367, 0x8478, 0x8658, 0x8525, 0x2412, 0x2301, 0x1503}, + {0x1636, 0x8367, 0x8478, 0x8658, 0x8525, 0x4514, 0x2301, 0x289A, 0x199B}, + {0x1503, 0x4514, 0x8525, 0x8658, 0x8478, 0x8367, 0x1636}, + {0x8525, 0x4445, 0x8478, 0x8367, 0x1636, 0x199B, 0x89AC}, + {0x2301, 0x1503, 0x1636, 0x8367, 0x8478, 0x4445, 0x8525, 0x89AC, 0x289A}, + {0x8525, 0x4445, 0x8478, 0x8367, 0x1636, 0x199B, 0x89AC, 0x2412, 0x2301, 0x4514}, + {0x2412, 0x4514, 0x1503, 0x1636, 0x8367, 0x8478, 0x4445, 0x8525, 0x89AC, 0x289A}, + {0x1636, 0x8367, 0x8478, 0x4445, 0x2412, 0x289A, 0x199B}, + {0x2412, 0x4445, 0x8478, 0x8367, 0x1636, 0x1503, 0x2301}, + {0x2301, 0x4514, 0x4445, 0x8478, 0x8367, 0x1636, 0x199B, 0x289A}, + {0x8367, 0x1636, 0x1503, 0x4514, 0x4445, 0x8478}, + {0x1636, 0x4647, 0x8478, 0x88BC, 0x199B}, + {0x8478, 0x4647, 0x1636, 0x1503, 0x2301, 0x289A, 0x88BC}, + {0x1636, 0x4647, 0x8478, 0x88BC, 0x199B, 0x2301, 0x2412, 0x4514}, + {0x2412, 0x4514, 0x1503, 0x1636, 0x4647, 0x8478, 0x88BC, 0x289A}, + {0x1636, 0x4647, 0x8478, 0x88BC, 0x199B, 0x8525, 0x2412, 0x289A, 0x89AC}, + {0x8525, 0x2412, 0x2301, 0x1503, 0x1636, 0x4647, 0x8478, 0x88BC, 0x89AC}, + {0x8525, 0x4514, 0x2301, 0x289A, 0x89AC, 0x1636, 0x4647, 0x8478, 0x88BC, 0x199B}, + {0x8478, 0x4647, 0x1636, 0x1503, 0x4514, 0x8525, 0x89AC, 0x88BC}, + {0x1636, 0x4647, 0x8478, 0x88BC, 0x199B, 0x8525, 0x8658, 0x4445}, + {0x8478, 0x4647, 0x1636, 0x1503, 0x2301, 0x289A, 0x88BC, 0x8658, 0x8525, 0x4445}, + {0x1636, 0x4647, 0x8478, 0x88BC, 0x199B, 0x8525, 0x8658, 0x4445, 0x2301, 0x2412, 0x4514}, + {0x2412, 0x4514, 0x1503, 0x1636, 0x4647, 0x8478, 0x88BC, 0x289A, 0x8525, 0x8658, 0x4445}, + {0x1636, 0x4647, 0x8478, 0x88BC, 0x199B, 0x8658, 0x4445, 0x2412, 0x289A, 0x89AC}, + {0x8658, 0x4445, 0x2412, 0x2301, 0x1503, 0x1636, 0x4647, 0x8478, 0x88BC, 0x89AC}, + {0x8658, 0x4445, 0x4514, 0x2301, 0x289A, 0x89AC, 0x1636, 0x4647, 0x8478, 0x88BC, 0x199B}, + {0x8478, 0x4647, 0x1636, 0x1503, 0x4514, 0x4445, 0x8658, 0x89AC, 0x88BC}, + {0x1636, 0x4647, 0x8658, 0x89AC, 0x199B}, + {0x2301, 0x1503, 0x1636, 0x4647, 0x8658, 0x89AC, 0x289A}, + {0x1636, 0x4647, 0x8658, 0x89AC, 0x199B, 0x2301, 0x2412, 0x4514}, + {0x2412, 0x4514, 0x1503, 0x1636, 0x4647, 0x8658, 0x89AC, 0x289A}, + {0x2412, 0x8525, 0x8658, 0x4647, 0x1636, 0x199B, 0x289A}, + {0x8658, 0x4647, 0x1636, 0x1503, 0x2301, 0x2412, 0x8525}, + {0x2301, 0x4514, 0x8525, 0x8658, 0x4647, 0x1636, 0x199B, 0x289A}, + {0x1503, 0x4514, 0x8525, 0x8658, 0x4647, 0x1636}, + {0x8525, 0x4445, 0x4647, 0x1636, 0x199B, 0x89AC}, + {0x8525, 0x4445, 0x4647, 0x1636, 0x1503, 0x2301, 0x289A, 0x89AC}, + {0x8525, 0x4445, 0x4647, 0x1636, 0x199B, 0x89AC, 0x2412, 0x2301, 0x4514}, + {0x2412, 0x4514, 0x1503, 0x1636, 0x4647, 0x4445, 0x8525, 0x89AC, 0x289A}, + {0x2412, 0x4445, 0x4647, 0x1636, 0x199B, 0x289A}, + {0x1503, 0x2301, 0x2412, 0x4445, 0x4647, 0x1636}, + {0x2301, 0x4514, 0x4445, 0x4647, 0x1636, 0x199B, 0x289A}, + {0x1503, 0x4514, 0x4445, 0x4647, 0x1636}, + {0x1636, 0x1503, 0x4334}, + {0x2301, 0x4334, 0x1636, 0x199B, 0x289A}, + {0x2301, 0x2412, 0x4514, 0x1636, 0x1503, 0x4334}, + {0x2412, 0x4514, 0x4334, 0x1636, 0x199B, 0x289A}, + {0x8525, 0x2412, 0x289A, 0x89AC, 0x1636, 0x1503, 0x4334}, + {0x1636, 0x4334, 0x2301, 0x2412, 0x8525, 0x89AC, 0x199B}, + {0x8525, 0x4514, 0x2301, 0x289A, 0x89AC, 0x1636, 0x1503, 0x4334}, + {0x1636, 0x4334, 0x4514, 0x8525, 0x89AC, 0x199B}, + {0x1636, 0x1503, 0x4334, 0x8525, 0x8658, 0x4445}, + {0x2301, 0x4334, 0x1636, 0x199B, 0x289A, 0x8525, 0x8658, 0x4445}, + {0x8525, 0x8658, 0x4445, 0x2301, 0x2412, 0x4514, 0x1636, 0x1503, 0x4334}, + {0x1636, 0x4334, 0x4514, 0x2412, 0x289A, 0x199B, 0x8658, 0x8525, 0x4445}, + {0x2412, 0x4445, 0x8658, 0x89AC, 0x289A, 0x1503, 0x1636, 0x4334}, + {0x8658, 0x4445, 0x2412, 0x2301, 0x4334, 0x1636, 0x199B, 0x89AC}, + {0x8658, 0x4445, 0x4514, 0x2301, 0x289A, 0x89AC, 0x1636, 0x1503, 0x4334}, + {0x1636, 0x4334, 0x4514, 0x4445, 0x8658, 0x89AC, 0x199B}, + {0x8658, 0x8478, 0x88BC, 0x89AC, 0x1503, 0x1636, 0x4334}, + {0x2301, 0x4334, 0x1636, 0x199B, 0x289A, 0x8478, 0x8658, 0x89AC, 0x88BC}, + {0x8478, 0x8658, 0x89AC, 0x88BC, 0x2301, 0x2412, 0x4514, 0x1636, 0x1503, 0x4334}, + {0x2412, 0x4514, 0x4334, 0x1636, 0x199B, 0x289A, 0x8478, 0x8658, 0x89AC, 0x88BC}, + {0x8478, 0x8658, 0x8525, 0x2412, 0x289A, 0x88BC, 0x1636, 0x1503, 0x4334}, + {0x8478, 0x8658, 0x8525, 0x2412, 0x2301, 0x4334, 0x1636, 0x199B, 0x88BC}, + {0x2301, 0x4514, 0x8525, 0x8658, 0x8478, 0x88BC, 0x289A, 0x1503, 0x1636, 0x4334}, + {0x1636, 0x4334, 0x4514, 0x8525, 0x8658, 0x8478, 0x88BC, 0x199B}, + {0x8478, 0x4445, 0x8525, 0x89AC, 0x88BC, 0x1636, 0x1503, 0x4334}, + {0x2301, 0x4334, 0x1636, 0x199B, 0x289A, 0x8478, 0x4445, 0x8525, 0x89AC, 0x88BC}, + {0x8478, 0x4445, 0x8525, 0x89AC, 0x88BC, 0x2301, 0x2412, 0x4514, 0x1636, 0x1503, 0x4334}, + {0x2412, 0x4514, 0x4334, 0x1636, 0x199B, 0x289A, 0x8478, 0x4445, 0x8525, 0x89AC, 0x88BC}, + {0x8478, 0x4445, 0x2412, 0x289A, 0x88BC, 0x1636, 0x1503, 0x4334}, + {0x1636, 0x4334, 0x2301, 0x2412, 0x4445, 0x8478, 0x88BC, 0x199B}, + {0x2301, 0x4514, 0x4445, 0x8478, 0x88BC, 0x289A, 0x1503, 0x1636, 0x4334}, + {0x1636, 0x4334, 0x4514, 0x4445, 0x8478, 0x88BC, 0x199B}, + {0x1636, 0x1503, 0x4334, 0x8478, 0x8367, 0x4647}, + {0x2301, 0x4334, 0x1636, 0x199B, 0x289A, 0x8478, 0x8367, 0x4647}, + {0x2301, 0x2412, 0x4514, 0x1636, 0x1503, 0x4334, 0x8478, 0x8367, 0x4647}, + {0x2412, 0x4514, 0x4334, 0x1636, 0x199B, 0x289A, 0x8478, 0x8367, 0x4647}, + {0x8525, 0x2412, 0x289A, 0x89AC, 0x1636, 0x1503, 0x4334, 0x8478, 0x8367, 0x4647}, + {0x1636, 0x4334, 0x2301, 0x2412, 0x8525, 0x89AC, 0x199B, 0x8367, 0x8478, 0x4647}, + {0x8525, 0x4514, 0x2301, 0x289A, 0x89AC, 0x1636, 0x1503, 0x4334, 0x8478, 0x8367, 0x4647}, + {0x1636, 0x4334, 0x4514, 0x8525, 0x89AC, 0x199B, 0x8367, 0x8478, 0x4647}, + {0x1636, 0x1503, 0x4334, 0x8478, 0x8367, 0x4647, 0x8525, 0x8658, 0x4445}, + {0x2301, 0x4334, 0x1636, 0x199B, 0x289A, 0x8478, 0x8367, 0x4647, 0x8525, 0x8658, 0x4445}, + {0x2301, 0x2412, 0x4514, 0x1636, 0x1503, 0x4334, 0x8478, 0x8367, 0x4647, 0x8525, 0x8658, 0x4445}, + {0x2412, 0x4514, 0x4334, 0x1636, 0x199B, 0x289A, 0x8478, 0x8367, 0x4647, 0x8525, 0x8658, 0x4445}, + {0x2412, 0x4445, 0x8658, 0x89AC, 0x289A, 0x8367, 0x8478, 0x4647, 0x1503, 0x1636, 0x4334}, + {0x8658, 0x4445, 0x2412, 0x2301, 0x4334, 0x1636, 0x199B, 0x89AC, 0x8478, 0x8367, 0x4647}, + {0x8658, 0x4445, 0x4514, 0x2301, 0x289A, 0x89AC, 0x1636, 0x1503, 0x4334, 0x8478, 0x8367, 0x4647}, + {0x8658, 0x4445, 0x4514, 0x4334, 0x1636, 0x199B, 0x89AC, 0x8478, 0x8367, 0x4647}, + {0x8658, 0x4647, 0x8367, 0x88BC, 0x89AC, 0x1503, 0x1636, 0x4334}, + {0x2301, 0x4334, 0x1636, 0x199B, 0x289A, 0x8367, 0x4647, 0x8658, 0x89AC, 0x88BC}, + {0x8658, 0x4647, 0x8367, 0x88BC, 0x89AC, 0x1503, 0x1636, 0x4334, 0x2412, 0x2301, 0x4514}, + {0x1636, 0x4334, 0x4514, 0x2412, 0x289A, 0x199B, 0x8658, 0x4647, 0x8367, 0x88BC, 0x89AC}, + {0x8367, 0x4647, 0x8658, 0x8525, 0x2412, 0x289A, 0x88BC, 0x1636, 0x1503, 0x4334}, + {0x8367, 0x4647, 0x8658, 0x8525, 0x2412, 0x2301, 0x4334, 0x1636, 0x199B, 0x88BC}, + {0x8367, 0x4647, 0x8658, 0x8525, 0x4514, 0x2301, 0x289A, 0x88BC, 0x1636, 0x1503, 0x4334}, + {0x8367, 0x4647, 0x8658, 0x8525, 0x4514, 0x4334, 0x1636, 0x199B, 0x88BC}, + {0x8525, 0x4445, 0x4647, 0x8367, 0x88BC, 0x89AC, 0x1503, 0x1636, 0x4334}, + {0x8367, 0x4647, 0x4445, 0x8525, 0x89AC, 0x88BC, 0x2301, 0x4334, 0x1636, 0x199B, 0x289A}, + {0x8367, 0x4647, 0x4445, 0x8525, 0x89AC, 0x88BC, 0x2301, 0x2412, 0x4514, 0x1636, 0x1503, 0x4334}, + {0x8367, 0x4647, 0x4445, 0x8525, 0x89AC, 0x88BC, 0x2412, 0x4514, 0x4334, 0x1636, 0x199B, 0x289A}, + {0x8367, 0x4647, 0x4445, 0x2412, 0x289A, 0x88BC, 0x1636, 0x1503, 0x4334}, + {0x1636, 0x4334, 0x2301, 0x2412, 0x4445, 0x4647, 0x8367, 0x88BC, 0x199B}, + {0x8367, 0x4647, 0x4445, 0x4514, 0x2301, 0x289A, 0x88BC, 0x1636, 0x1503, 0x4334}, + {0x8367, 0x4647, 0x4445, 0x4514, 0x4334, 0x1636, 0x199B, 0x88BC}, + {0x8367, 0x4334, 0x1503, 0x199B, 0x88BC}, + {0x2301, 0x4334, 0x8367, 0x88BC, 0x289A}, + {0x8367, 0x4334, 0x1503, 0x199B, 0x88BC, 0x2412, 0x2301, 0x4514}, + {0x2412, 0x4514, 0x4334, 0x8367, 0x88BC, 0x289A}, + {0x8367, 0x4334, 0x1503, 0x199B, 0x88BC, 0x2412, 0x8525, 0x89AC, 0x289A}, + {0x8525, 0x2412, 0x2301, 0x4334, 0x8367, 0x88BC, 0x89AC}, + {0x8525, 0x4514, 0x2301, 0x289A, 0x89AC, 0x1503, 0x4334, 0x8367, 0x88BC, 0x199B}, + {0x8367, 0x4334, 0x4514, 0x8525, 0x89AC, 0x88BC}, + {0x8367, 0x4334, 0x1503, 0x199B, 0x88BC, 0x8658, 0x8525, 0x4445}, + {0x2301, 0x4334, 0x8367, 0x88BC, 0x289A, 0x8525, 0x8658, 0x4445}, + {0x8367, 0x4334, 0x1503, 0x199B, 0x88BC, 0x2412, 0x2301, 0x4514, 0x8658, 0x8525, 0x4445}, + {0x2412, 0x4514, 0x4334, 0x8367, 0x88BC, 0x289A, 0x8525, 0x8658, 0x4445}, + {0x2412, 0x4445, 0x8658, 0x89AC, 0x289A, 0x8367, 0x4334, 0x1503, 0x199B, 0x88BC}, + {0x8658, 0x4445, 0x2412, 0x2301, 0x4334, 0x8367, 0x88BC, 0x89AC}, + {0x2301, 0x4514, 0x4445, 0x8658, 0x89AC, 0x289A, 0x8367, 0x4334, 0x1503, 0x199B, 0x88BC}, + {0x8658, 0x4445, 0x4514, 0x4334, 0x8367, 0x88BC, 0x89AC}, + {0x1503, 0x4334, 0x8367, 0x8478, 0x8658, 0x89AC, 0x199B}, + {0x8658, 0x8478, 0x8367, 0x4334, 0x2301, 0x289A, 0x89AC}, + {0x1503, 0x4334, 0x8367, 0x8478, 0x8658, 0x89AC, 0x199B, 0x2301, 0x2412, 0x4514}, + {0x2412, 0x4514, 0x4334, 0x8367, 0x8478, 0x8658, 0x89AC, 0x289A}, + {0x2412, 0x8525, 0x8658, 0x8478, 0x8367, 0x4334, 0x1503, 0x199B, 0x289A}, + {0x8367, 0x4334, 0x2301, 0x2412, 0x8525, 0x8658, 0x8478}, + {0x1503, 0x4334, 0x8367, 0x8478, 0x8658, 0x8525, 0x4514, 0x2301, 0x289A, 0x199B}, + {0x8658, 0x8478, 0x8367, 0x4334, 0x4514, 0x8525}, + {0x1503, 0x4334, 0x8367, 0x8478, 0x4445, 0x8525, 0x89AC, 0x199B}, + {0x8525, 0x4445, 0x8478, 0x8367, 0x4334, 0x2301, 0x289A, 0x89AC}, + {0x1503, 0x4334, 0x8367, 0x8478, 0x4445, 0x8525, 0x89AC, 0x199B, 0x2301, 0x2412, 0x4514}, + {0x8525, 0x4445, 0x8478, 0x8367, 0x4334, 0x4514, 0x2412, 0x289A, 0x89AC}, + {0x1503, 0x4334, 0x8367, 0x8478, 0x4445, 0x2412, 0x289A, 0x199B}, + {0x8367, 0x4334, 0x2301, 0x2412, 0x4445, 0x8478}, + {0x1503, 0x4334, 0x8367, 0x8478, 0x4445, 0x4514, 0x2301, 0x289A, 0x199B}, + {0x8367, 0x4334, 0x4514, 0x4445, 0x8478}, + {0x1503, 0x4334, 0x4647, 0x8478, 0x88BC, 0x199B}, + {0x8478, 0x4647, 0x4334, 0x2301, 0x289A, 0x88BC}, + {0x8478, 0x4647, 0x4334, 0x1503, 0x199B, 0x88BC, 0x2412, 0x2301, 0x4514}, + {0x8478, 0x4647, 0x4334, 0x4514, 0x2412, 0x289A, 0x88BC}, + {0x1503, 0x4334, 0x4647, 0x8478, 0x88BC, 0x199B, 0x8525, 0x2412, 0x289A, 0x89AC}, + {0x8478, 0x4647, 0x4334, 0x2301, 0x2412, 0x8525, 0x89AC, 0x88BC}, + {0x1503, 0x4334, 0x4647, 0x8478, 0x88BC, 0x199B, 0x8525, 0x4514, 0x2301, 0x289A, 0x89AC}, + {0x8478, 0x4647, 0x4334, 0x4514, 0x8525, 0x89AC, 0x88BC}, + {0x1503, 0x4334, 0x4647, 0x8478, 0x88BC, 0x199B, 0x8525, 0x8658, 0x4445}, + {0x8478, 0x4647, 0x4334, 0x2301, 0x289A, 0x88BC, 0x8658, 0x8525, 0x4445}, + {0x1503, 0x4334, 0x4647, 0x8478, 0x88BC, 0x199B, 0x8525, 0x8658, 0x4445, 0x2301, 0x2412, 0x4514}, + {0x2412, 0x4514, 0x4334, 0x4647, 0x8478, 0x88BC, 0x289A, 0x8525, 0x8658, 0x4445}, + {0x8478, 0x4647, 0x4334, 0x1503, 0x199B, 0x88BC, 0x2412, 0x4445, 0x8658, 0x89AC, 0x289A}, + {0x8658, 0x4445, 0x2412, 0x2301, 0x4334, 0x4647, 0x8478, 0x88BC, 0x89AC}, + {0x8658, 0x4445, 0x4514, 0x2301, 0x289A, 0x89AC, 0x1503, 0x4334, 0x4647, 0x8478, 0x88BC, 0x199B}, + {0x8658, 0x4445, 0x4514, 0x4334, 0x4647, 0x8478, 0x88BC, 0x89AC}, + {0x1503, 0x4334, 0x4647, 0x8658, 0x89AC, 0x199B}, + {0x8658, 0x4647, 0x4334, 0x2301, 0x289A, 0x89AC}, + {0x1503, 0x4334, 0x4647, 0x8658, 0x89AC, 0x199B, 0x2301, 0x2412, 0x4514}, + {0x2412, 0x4514, 0x4334, 0x4647, 0x8658, 0x89AC, 0x289A}, + {0x1503, 0x4334, 0x4647, 0x8658, 0x8525, 0x2412, 0x289A, 0x199B}, + {0x2412, 0x8525, 0x8658, 0x4647, 0x4334, 0x2301}, + {0x2301, 0x4514, 0x8525, 0x8658, 0x4647, 0x4334, 0x1503, 0x199B, 0x289A}, + {0x8658, 0x4647, 0x4334, 0x4514, 0x8525}, + {0x8525, 0x4445, 0x4647, 0x4334, 0x1503, 0x199B, 0x89AC}, + {0x8525, 0x4445, 0x4647, 0x4334, 0x2301, 0x289A, 0x89AC}, + {0x1503, 0x4334, 0x4647, 0x4445, 0x8525, 0x89AC, 0x199B, 0x2301, 0x2412, 0x4514}, + {0x2412, 0x4514, 0x4334, 0x4647, 0x4445, 0x8525, 0x89AC, 0x289A}, + {0x1503, 0x4334, 0x4647, 0x4445, 0x2412, 0x289A, 0x199B}, + {0x2412, 0x4445, 0x4647, 0x4334, 0x2301}, + {0x1503, 0x4334, 0x4647, 0x4445, 0x4514, 0x2301, 0x289A, 0x199B}, + {0x4514, 0x4445, 0x4647, 0x4334}, + {0x4514, 0x4445, 0x4647, 0x4334}, + {0x2301, 0x1503, 0x199B, 0x289A, 0x4334, 0x4514, 0x4445, 0x4647}, + {0x2412, 0x4445, 0x4647, 0x4334, 0x2301}, + {0x1503, 0x4334, 0x4647, 0x4445, 0x2412, 0x289A, 0x199B}, + {0x8525, 0x2412, 0x289A, 0x89AC, 0x4514, 0x4445, 0x4647, 0x4334}, + {0x8525, 0x2412, 0x2301, 0x1503, 0x199B, 0x89AC, 0x4514, 0x4445, 0x4647, 0x4334}, + {0x8525, 0x4445, 0x4647, 0x4334, 0x2301, 0x289A, 0x89AC}, + {0x8525, 0x4445, 0x4647, 0x4334, 0x1503, 0x199B, 0x89AC}, + {0x8658, 0x4647, 0x4334, 0x4514, 0x8525}, + {0x1503, 0x2301, 0x289A, 0x199B, 0x8525, 0x4514, 0x4334, 0x4647, 0x8658}, + {0x2412, 0x8525, 0x8658, 0x4647, 0x4334, 0x2301}, + {0x1503, 0x4334, 0x4647, 0x8658, 0x8525, 0x2412, 0x289A, 0x199B}, + {0x2412, 0x4514, 0x4334, 0x4647, 0x8658, 0x89AC, 0x289A}, + {0x8658, 0x4647, 0x4334, 0x4514, 0x2412, 0x2301, 0x1503, 0x199B, 0x89AC}, + {0x8658, 0x4647, 0x4334, 0x2301, 0x289A, 0x89AC}, + {0x1503, 0x4334, 0x4647, 0x8658, 0x89AC, 0x199B}, + {0x8478, 0x8658, 0x89AC, 0x88BC, 0x4445, 0x4647, 0x4334, 0x4514}, + {0x2301, 0x1503, 0x199B, 0x289A, 0x8478, 0x8658, 0x89AC, 0x88BC, 0x4334, 0x4514, 0x4445, 0x4647}, + {0x8478, 0x8658, 0x89AC, 0x88BC, 0x2412, 0x4445, 0x4647, 0x4334, 0x2301}, + {0x1503, 0x4334, 0x4647, 0x4445, 0x2412, 0x289A, 0x199B, 0x8658, 0x8478, 0x88BC, 0x89AC}, + {0x8478, 0x8658, 0x8525, 0x2412, 0x289A, 0x88BC, 0x4445, 0x4647, 0x4334, 0x4514}, + {0x8478, 0x8658, 0x8525, 0x2412, 0x2301, 0x1503, 0x199B, 0x88BC, 0x4514, 0x4445, 0x4647, 0x4334}, + {0x2301, 0x4334, 0x4647, 0x4445, 0x8525, 0x8658, 0x8478, 0x88BC, 0x289A}, + {0x8478, 0x8658, 0x8525, 0x4445, 0x4647, 0x4334, 0x1503, 0x199B, 0x88BC}, + {0x8478, 0x4647, 0x4334, 0x4514, 0x8525, 0x89AC, 0x88BC}, + {0x8478, 0x4647, 0x4334, 0x4514, 0x8525, 0x89AC, 0x88BC, 0x2301, 0x1503, 0x199B, 0x289A}, + {0x8478, 0x4647, 0x4334, 0x2301, 0x2412, 0x8525, 0x89AC, 0x88BC}, + {0x8478, 0x4647, 0x4334, 0x1503, 0x2412, 0x8525, 0x199B, 0x289A, 0x89AC, 0x88BC}, + {0x8478, 0x4647, 0x4334, 0x4514, 0x2412, 0x289A, 0x88BC}, + {0x1503, 0x2301, 0x2412, 0x4514, 0x4334, 0x4647, 0x8478, 0x88BC, 0x199B}, + {0x8478, 0x4647, 0x4334, 0x2301, 0x289A, 0x88BC}, + {0x1503, 0x4334, 0x4647, 0x8478, 0x88BC, 0x199B}, + {0x8367, 0x4334, 0x4514, 0x4445, 0x8478}, + {0x2301, 0x1503, 0x199B, 0x289A, 0x8367, 0x4334, 0x4514, 0x4445, 0x8478}, + {0x8367, 0x4334, 0x2301, 0x2412, 0x4445, 0x8478}, + {0x1503, 0x4334, 0x8367, 0x8478, 0x4445, 0x2412, 0x289A, 0x199B}, + {0x2412, 0x8525, 0x89AC, 0x289A, 0x8478, 0x4445, 0x4514, 0x4334, 0x8367}, + {0x8525, 0x2412, 0x2301, 0x1503, 0x199B, 0x89AC, 0x8367, 0x4334, 0x4514, 0x4445, 0x8478}, + {0x8525, 0x4445, 0x8478, 0x8367, 0x4334, 0x2301, 0x289A, 0x89AC}, + {0x1503, 0x4334, 0x8367, 0x8478, 0x4445, 0x8525, 0x89AC, 0x199B}, + {0x8658, 0x8478, 0x8367, 0x4334, 0x4514, 0x8525}, + {0x2301, 0x1503, 0x199B, 0x289A, 0x8367, 0x4334, 0x4514, 0x8525, 0x8658, 0x8478}, + {0x8367, 0x4334, 0x2301, 0x2412, 0x8525, 0x8658, 0x8478}, + {0x2412, 0x8525, 0x8658, 0x8478, 0x8367, 0x4334, 0x1503, 0x199B, 0x289A}, + {0x2412, 0x4514, 0x4334, 0x8367, 0x8478, 0x8658, 0x89AC, 0x289A}, + {0x8658, 0x8478, 0x8367, 0x4334, 0x4514, 0x2412, 0x2301, 0x1503, 0x199B, 0x89AC}, + {0x8658, 0x8478, 0x8367, 0x4334, 0x2301, 0x289A, 0x89AC}, + {0x1503, 0x4334, 0x8367, 0x8478, 0x8658, 0x89AC, 0x199B}, + {0x8658, 0x4445, 0x4514, 0x4334, 0x8367, 0x88BC, 0x89AC}, + {0x8658, 0x4445, 0x4514, 0x4334, 0x8367, 0x88BC, 0x89AC, 0x1503, 0x2301, 0x289A, 0x199B}, + {0x8658, 0x4445, 0x2412, 0x2301, 0x4334, 0x8367, 0x88BC, 0x89AC}, + {0x8658, 0x4445, 0x2412, 0x1503, 0x4334, 0x8367, 0x289A, 0x199B, 0x88BC, 0x89AC}, + {0x8367, 0x4334, 0x4514, 0x4445, 0x8658, 0x8525, 0x2412, 0x289A, 0x88BC}, + {0x1503, 0x2301, 0x2412, 0x8525, 0x8658, 0x4445, 0x4514, 0x4334, 0x8367, 0x88BC, 0x199B}, + {0x2301, 0x4334, 0x8367, 0x88BC, 0x289A, 0x8525, 0x8658, 0x4445}, + {0x8367, 0x4334, 0x1503, 0x199B, 0x88BC, 0x8658, 0x8525, 0x4445}, + {0x8367, 0x4334, 0x4514, 0x8525, 0x89AC, 0x88BC}, + {0x8367, 0x4334, 0x4514, 0x8525, 0x89AC, 0x88BC, 0x2301, 0x1503, 0x199B, 0x289A}, + {0x8525, 0x2412, 0x2301, 0x4334, 0x8367, 0x88BC, 0x89AC}, + {0x1503, 0x4334, 0x8367, 0x8525, 0x2412, 0x88BC, 0x89AC, 0x289A, 0x199B}, + {0x2412, 0x4514, 0x4334, 0x8367, 0x88BC, 0x289A}, + {0x1503, 0x2301, 0x2412, 0x4514, 0x4334, 0x8367, 0x88BC, 0x199B}, + {0x2301, 0x4334, 0x8367, 0x88BC, 0x289A}, + {0x8367, 0x4334, 0x1503, 0x199B, 0x88BC}, + {0x1636, 0x8367, 0x88BC, 0x199B, 0x4647, 0x4334, 0x4514, 0x4445}, + {0x2301, 0x1503, 0x1636, 0x8367, 0x88BC, 0x289A, 0x4334, 0x4514, 0x4445, 0x4647}, + {0x8367, 0x1636, 0x199B, 0x88BC, 0x2301, 0x4334, 0x4647, 0x4445, 0x2412}, + {0x2412, 0x4445, 0x4647, 0x4334, 0x1503, 0x1636, 0x8367, 0x88BC, 0x289A}, + {0x8525, 0x2412, 0x289A, 0x89AC, 0x1636, 0x8367, 0x88BC, 0x199B, 0x4514, 0x4445, 0x4647, 0x4334}, + {0x8525, 0x2412, 0x2301, 0x1503, 0x1636, 0x8367, 0x88BC, 0x89AC, 0x4334, 0x4514, 0x4445, 0x4647}, + {0x8525, 0x4445, 0x4647, 0x4334, 0x2301, 0x289A, 0x89AC, 0x1636, 0x8367, 0x88BC, 0x199B}, + {0x8367, 0x1636, 0x1503, 0x4334, 0x4647, 0x4445, 0x8525, 0x89AC, 0x88BC}, + {0x1636, 0x8367, 0x88BC, 0x199B, 0x8658, 0x4647, 0x4334, 0x4514, 0x8525}, + {0x2301, 0x1503, 0x1636, 0x8367, 0x88BC, 0x289A, 0x8658, 0x4647, 0x4334, 0x4514, 0x8525}, + {0x1636, 0x8367, 0x88BC, 0x199B, 0x8658, 0x4647, 0x4334, 0x2301, 0x2412, 0x8525}, + {0x2412, 0x8525, 0x8658, 0x4647, 0x4334, 0x1503, 0x1636, 0x8367, 0x88BC, 0x289A}, + {0x2412, 0x4514, 0x4334, 0x4647, 0x8658, 0x89AC, 0x289A, 0x8367, 0x1636, 0x199B, 0x88BC}, + {0x8367, 0x1636, 0x1503, 0x2301, 0x2412, 0x4514, 0x4334, 0x4647, 0x8658, 0x89AC, 0x88BC}, + {0x8658, 0x4647, 0x4334, 0x2301, 0x289A, 0x89AC, 0x1636, 0x8367, 0x88BC, 0x199B}, + {0x8367, 0x1636, 0x1503, 0x4334, 0x4647, 0x8658, 0x89AC, 0x88BC}, + {0x1636, 0x8367, 0x8478, 0x8658, 0x89AC, 0x199B, 0x4647, 0x4334, 0x4514, 0x4445}, + {0x2301, 0x1503, 0x1636, 0x8367, 0x8478, 0x8658, 0x89AC, 0x289A, 0x4647, 0x4334, 0x4514, 0x4445}, + {0x1636, 0x8367, 0x8478, 0x8658, 0x89AC, 0x199B, 0x2412, 0x4445, 0x4647, 0x4334, 0x2301}, + {0x8658, 0x8478, 0x8367, 0x1636, 0x1503, 0x4334, 0x4647, 0x4445, 0x2412, 0x289A, 0x89AC}, + {0x1636, 0x8367, 0x8478, 0x8658, 0x8525, 0x2412, 0x289A, 0x199B, 0x4445, 0x4647, 0x4334, 0x4514}, + {0x2301, 0x1503, 0x1636, 0x8367, 0x8478, 0x8658, 0x8525, 0x2412, 0x4334, 0x4514, 0x4445, 0x4647}, + {0x1636, 0x8367, 0x8478, 0x8658, 0x8525, 0x4445, 0x4647, 0x4334, 0x2301, 0x289A, 0x199B}, + {0x1503, 0x1636, 0x8367, 0x8478, 0x8658, 0x8525, 0x4445, 0x4647, 0x4334}, + {0x8525, 0x4514, 0x4334, 0x4647, 0x8478, 0x8367, 0x1636, 0x199B, 0x89AC}, + {0x2301, 0x1503, 0x1636, 0x8367, 0x8478, 0x4647, 0x4334, 0x4514, 0x8525, 0x89AC, 0x289A}, + {0x8525, 0x2412, 0x2301, 0x4334, 0x4647, 0x8478, 0x8367, 0x1636, 0x199B, 0x89AC}, + {0x2412, 0x8525, 0x89AC, 0x289A, 0x1503, 0x1636, 0x8367, 0x8478, 0x4647, 0x4334}, + {0x1636, 0x8367, 0x8478, 0x4647, 0x4334, 0x4514, 0x2412, 0x289A, 0x199B}, + {0x2412, 0x2301, 0x1503, 0x1636, 0x8367, 0x8478, 0x4647, 0x4334, 0x4514}, + {0x1636, 0x8367, 0x8478, 0x4647, 0x4334, 0x2301, 0x289A, 0x199B}, + {0x1636, 0x8367, 0x8478, 0x4647, 0x4334, 0x1503}, + {0x1636, 0x4334, 0x4514, 0x4445, 0x8478, 0x88BC, 0x199B}, + {0x8478, 0x4445, 0x4514, 0x4334, 0x1636, 0x1503, 0x2301, 0x289A, 0x88BC}, + {0x1636, 0x4334, 0x2301, 0x2412, 0x4445, 0x8478, 0x88BC, 0x199B}, + {0x8478, 0x4445, 0x2412, 0x289A, 0x88BC, 0x1636, 0x1503, 0x4334}, + {0x1636, 0x4334, 0x4514, 0x4445, 0x8478, 0x88BC, 0x199B, 0x8525, 0x2412, 0x289A, 0x89AC}, + {0x8525, 0x2412, 0x2301, 0x1503, 0x1636, 0x4334, 0x4514, 0x4445, 0x8478, 0x88BC, 0x89AC}, + {0x1636, 0x4334, 0x2301, 0x8525, 0x4445, 0x8478, 0x289A, 0x89AC, 0x88BC, 0x199B}, + {0x8478, 0x4445, 0x8525, 0x89AC, 0x88BC, 0x1636, 0x1503, 0x4334}, + {0x1636, 0x4334, 0x4514, 0x8525, 0x8658, 0x8478, 0x88BC, 0x199B}, + {0x8478, 0x8658, 0x8525, 0x4514, 0x4334, 0x1636, 0x1503, 0x2301, 0x289A, 0x88BC}, + {0x8478, 0x8658, 0x8525, 0x2412, 0x2301, 0x4334, 0x1636, 0x199B, 0x88BC}, + {0x8478, 0x8658, 0x8525, 0x2412, 0x289A, 0x88BC, 0x1636, 0x1503, 0x4334}, + {0x1636, 0x4334, 0x4514, 0x2412, 0x8658, 0x8478, 0x289A, 0x89AC, 0x88BC, 0x199B}, + {0x8658, 0x8478, 0x88BC, 0x89AC, 0x2412, 0x2301, 0x1503, 0x1636, 0x4334, 0x4514}, + {0x1636, 0x4334, 0x2301, 0x8658, 0x8478, 0x289A, 0x89AC, 0x88BC, 0x199B}, + {0x8658, 0x8478, 0x88BC, 0x89AC, 0x1503, 0x1636, 0x4334}, + {0x1636, 0x4334, 0x4514, 0x4445, 0x8658, 0x89AC, 0x199B}, + {0x2301, 0x1503, 0x1636, 0x4334, 0x4514, 0x4445, 0x8658, 0x89AC, 0x289A}, + {0x8658, 0x4445, 0x2412, 0x2301, 0x4334, 0x1636, 0x199B, 0x89AC}, + {0x2412, 0x4445, 0x8658, 0x89AC, 0x289A, 0x1503, 0x1636, 0x4334}, + {0x2412, 0x8525, 0x8658, 0x4445, 0x4514, 0x4334, 0x1636, 0x199B, 0x289A}, + {0x8658, 0x8525, 0x2412, 0x2301, 0x1503, 0x1636, 0x4334, 0x4514, 0x4445}, + {0x2301, 0x4334, 0x1636, 0x199B, 0x289A, 0x8525, 0x8658, 0x4445}, + {0x1636, 0x1503, 0x4334, 0x8525, 0x8658, 0x4445}, + {0x1636, 0x4334, 0x4514, 0x8525, 0x89AC, 0x199B}, + {0x2301, 0x1503, 0x1636, 0x4334, 0x4514, 0x8525, 0x89AC, 0x289A}, + {0x1636, 0x4334, 0x2301, 0x2412, 0x8525, 0x89AC, 0x199B}, + {0x8525, 0x2412, 0x289A, 0x89AC, 0x1636, 0x1503, 0x4334}, + {0x2412, 0x4514, 0x4334, 0x1636, 0x199B, 0x289A}, + {0x2301, 0x1503, 0x1636, 0x4334, 0x4514, 0x2412}, + {0x2301, 0x4334, 0x1636, 0x199B, 0x289A}, + {0x1636, 0x1503, 0x4334}, + {0x1503, 0x4514, 0x4445, 0x4647, 0x1636}, + {0x2301, 0x4514, 0x4445, 0x4647, 0x1636, 0x199B, 0x289A}, + {0x1503, 0x2301, 0x2412, 0x4445, 0x4647, 0x1636}, + {0x2412, 0x4445, 0x4647, 0x1636, 0x199B, 0x289A}, + {0x8525, 0x2412, 0x289A, 0x89AC, 0x1503, 0x4514, 0x4445, 0x4647, 0x1636}, + {0x1636, 0x4647, 0x4445, 0x4514, 0x2301, 0x2412, 0x8525, 0x89AC, 0x199B}, + {0x8525, 0x4445, 0x4647, 0x1636, 0x1503, 0x2301, 0x289A, 0x89AC}, + {0x8525, 0x4445, 0x4647, 0x1636, 0x199B, 0x89AC}, + {0x1503, 0x4514, 0x8525, 0x8658, 0x4647, 0x1636}, + {0x2301, 0x4514, 0x8525, 0x8658, 0x4647, 0x1636, 0x199B, 0x289A}, + {0x8658, 0x4647, 0x1636, 0x1503, 0x2301, 0x2412, 0x8525}, + {0x2412, 0x8525, 0x8658, 0x4647, 0x1636, 0x199B, 0x289A}, + {0x2412, 0x4514, 0x1503, 0x1636, 0x4647, 0x8658, 0x89AC, 0x289A}, + {0x1636, 0x4647, 0x8658, 0x89AC, 0x199B, 0x2301, 0x2412, 0x4514}, + {0x2301, 0x1503, 0x1636, 0x4647, 0x8658, 0x89AC, 0x289A}, + {0x1636, 0x4647, 0x8658, 0x89AC, 0x199B}, + {0x8658, 0x8478, 0x88BC, 0x89AC, 0x1636, 0x4647, 0x4445, 0x4514, 0x1503}, + {0x2301, 0x4514, 0x4445, 0x4647, 0x1636, 0x199B, 0x289A, 0x8478, 0x8658, 0x89AC, 0x88BC}, + {0x8478, 0x8658, 0x89AC, 0x88BC, 0x2412, 0x4445, 0x4647, 0x1636, 0x1503, 0x2301}, + {0x2412, 0x4445, 0x4647, 0x1636, 0x199B, 0x289A, 0x8478, 0x8658, 0x89AC, 0x88BC}, + {0x8478, 0x8658, 0x8525, 0x2412, 0x289A, 0x88BC, 0x1503, 0x4514, 0x4445, 0x4647, 0x1636}, + {0x8478, 0x8658, 0x8525, 0x2412, 0x2301, 0x4514, 0x4445, 0x4647, 0x1636, 0x199B, 0x88BC}, + {0x2301, 0x1503, 0x1636, 0x4647, 0x4445, 0x8525, 0x8658, 0x8478, 0x88BC, 0x289A}, + {0x8478, 0x8658, 0x8525, 0x4445, 0x4647, 0x1636, 0x199B, 0x88BC}, + {0x8478, 0x4647, 0x1636, 0x1503, 0x4514, 0x8525, 0x89AC, 0x88BC}, + {0x2301, 0x4514, 0x8525, 0x8478, 0x4647, 0x1636, 0x89AC, 0x88BC, 0x199B, 0x289A}, + {0x8525, 0x2412, 0x2301, 0x1503, 0x1636, 0x4647, 0x8478, 0x88BC, 0x89AC}, + {0x8478, 0x4647, 0x1636, 0x2412, 0x8525, 0x199B, 0x289A, 0x89AC, 0x88BC}, + {0x2412, 0x4514, 0x1503, 0x1636, 0x4647, 0x8478, 0x88BC, 0x289A}, + {0x1636, 0x4647, 0x8478, 0x88BC, 0x199B, 0x2301, 0x2412, 0x4514}, + {0x8478, 0x4647, 0x1636, 0x1503, 0x2301, 0x289A, 0x88BC}, + {0x1636, 0x4647, 0x8478, 0x88BC, 0x199B}, + {0x8367, 0x1636, 0x1503, 0x4514, 0x4445, 0x8478}, + {0x2301, 0x4514, 0x4445, 0x8478, 0x8367, 0x1636, 0x199B, 0x289A}, + {0x2412, 0x4445, 0x8478, 0x8367, 0x1636, 0x1503, 0x2301}, + {0x1636, 0x8367, 0x8478, 0x4445, 0x2412, 0x289A, 0x199B}, + {0x8525, 0x2412, 0x289A, 0x89AC, 0x1503, 0x4514, 0x4445, 0x8478, 0x8367, 0x1636}, + {0x1636, 0x8367, 0x8478, 0x4445, 0x4514, 0x2301, 0x2412, 0x8525, 0x89AC, 0x199B}, + {0x2301, 0x1503, 0x1636, 0x8367, 0x8478, 0x4445, 0x8525, 0x89AC, 0x289A}, + {0x8525, 0x4445, 0x8478, 0x8367, 0x1636, 0x199B, 0x89AC}, + {0x1503, 0x4514, 0x8525, 0x8658, 0x8478, 0x8367, 0x1636}, + {0x1636, 0x8367, 0x8478, 0x8658, 0x8525, 0x4514, 0x2301, 0x289A, 0x199B}, + {0x1636, 0x8367, 0x8478, 0x8658, 0x8525, 0x2412, 0x2301, 0x1503}, + {0x1636, 0x8367, 0x8478, 0x8658, 0x8525, 0x2412, 0x289A, 0x199B}, + {0x8658, 0x8478, 0x8367, 0x1636, 0x1503, 0x4514, 0x2412, 0x289A, 0x89AC}, + {0x1636, 0x8367, 0x8478, 0x8658, 0x89AC, 0x199B, 0x2301, 0x2412, 0x4514}, + {0x2301, 0x1503, 0x1636, 0x8367, 0x8478, 0x8658, 0x89AC, 0x289A}, + {0x1636, 0x8367, 0x8478, 0x8658, 0x89AC, 0x199B}, + {0x8658, 0x4445, 0x4514, 0x1503, 0x1636, 0x8367, 0x88BC, 0x89AC}, + {0x2301, 0x4514, 0x4445, 0x8658, 0x8367, 0x1636, 0x89AC, 0x88BC, 0x199B, 0x289A}, + {0x8367, 0x1636, 0x1503, 0x2301, 0x2412, 0x4445, 0x8658, 0x89AC, 0x88BC}, + {0x8658, 0x4445, 0x2412, 0x1636, 0x8367, 0x289A, 0x199B, 0x88BC, 0x89AC}, + {0x8367, 0x1636, 0x1503, 0x4514, 0x4445, 0x8658, 0x8525, 0x2412, 0x289A, 0x88BC}, + {0x8367, 0x1636, 0x199B, 0x88BC, 0x8658, 0x8525, 0x2412, 0x2301, 0x4514, 0x4445}, + {0x2301, 0x1503, 0x1636, 0x8367, 0x88BC, 0x289A, 0x8525, 0x8658, 0x4445}, + {0x1636, 0x8367, 0x88BC, 0x199B, 0x8525, 0x8658, 0x4445}, + {0x8367, 0x1636, 0x1503, 0x4514, 0x8525, 0x89AC, 0x88BC}, + {0x2301, 0x4514, 0x8525, 0x8367, 0x1636, 0x89AC, 0x88BC, 0x199B, 0x289A}, + {0x8525, 0x2412, 0x2301, 0x1503, 0x1636, 0x8367, 0x88BC, 0x89AC}, + {0x2412, 0x8525, 0x8367, 0x1636, 0x89AC, 0x88BC, 0x199B, 0x289A}, + {0x2412, 0x4514, 0x1503, 0x1636, 0x8367, 0x88BC, 0x289A}, + {0x8367, 0x1636, 0x199B, 0x88BC, 0x2412, 0x2301, 0x4514}, + {0x2301, 0x1503, 0x1636, 0x8367, 0x88BC, 0x289A}, + {0x1636, 0x8367, 0x88BC, 0x199B}, + {0x8367, 0x4647, 0x4445, 0x4514, 0x1503, 0x199B, 0x88BC}, + {0x2301, 0x4514, 0x4445, 0x4647, 0x8367, 0x88BC, 0x289A}, + {0x8367, 0x4647, 0x4445, 0x2412, 0x2301, 0x1503, 0x199B, 0x88BC}, + {0x8367, 0x4647, 0x4445, 0x2412, 0x289A, 0x88BC}, + {0x8367, 0x4647, 0x4445, 0x4514, 0x1503, 0x199B, 0x88BC, 0x2412, 0x8525, 0x89AC, 0x289A}, + {0x8525, 0x2412, 0x2301, 0x4514, 0x4445, 0x4647, 0x8367, 0x88BC, 0x89AC}, + {0x8525, 0x4445, 0x4647, 0x8367, 0x1503, 0x2301, 0x88BC, 0x199B, 0x289A, 0x89AC}, + {0x8367, 0x4647, 0x4445, 0x8525, 0x89AC, 0x88BC}, + {0x8367, 0x4647, 0x8658, 0x8525, 0x4514, 0x1503, 0x199B, 0x88BC}, + {0x8367, 0x4647, 0x8658, 0x8525, 0x4514, 0x2301, 0x289A, 0x88BC}, + {0x1503, 0x2301, 0x2412, 0x8525, 0x8658, 0x4647, 0x8367, 0x88BC, 0x199B}, + {0x8367, 0x4647, 0x8658, 0x8525, 0x2412, 0x289A, 0x88BC}, + {0x2412, 0x4514, 0x1503, 0x8367, 0x4647, 0x8658, 0x199B, 0x88BC, 0x89AC, 0x289A}, + {0x8658, 0x4647, 0x8367, 0x88BC, 0x89AC, 0x2412, 0x2301, 0x4514}, + {0x8367, 0x4647, 0x8658, 0x2301, 0x1503, 0x89AC, 0x289A, 0x199B, 0x88BC}, + {0x8658, 0x4647, 0x8367, 0x88BC, 0x89AC}, + {0x1503, 0x4514, 0x4445, 0x4647, 0x8367, 0x8478, 0x8658, 0x89AC, 0x199B}, + {0x8658, 0x8478, 0x8367, 0x4647, 0x4445, 0x4514, 0x2301, 0x289A, 0x89AC}, + {0x1503, 0x2301, 0x2412, 0x4445, 0x4647, 0x8367, 0x8478, 0x8658, 0x89AC, 0x199B}, + {0x8658, 0x8478, 0x8367, 0x4647, 0x4445, 0x2412, 0x289A, 0x89AC}, + {0x2412, 0x8525, 0x8658, 0x8478, 0x8367, 0x4647, 0x4445, 0x4514, 0x1503, 0x199B, 0x289A}, + {0x8367, 0x8478, 0x8658, 0x8525, 0x2412, 0x2301, 0x4514, 0x4445, 0x4647}, + {0x1503, 0x2301, 0x289A, 0x199B, 0x8367, 0x8478, 0x8658, 0x8525, 0x4445, 0x4647}, + {0x8478, 0x8658, 0x8525, 0x4445, 0x4647, 0x8367}, + {0x8525, 0x4514, 0x1503, 0x199B, 0x89AC, 0x8478, 0x8367, 0x4647}, + {0x8525, 0x4514, 0x2301, 0x289A, 0x89AC, 0x8478, 0x8367, 0x4647}, + {0x8525, 0x2412, 0x2301, 0x1503, 0x199B, 0x89AC, 0x8478, 0x8367, 0x4647}, + {0x2412, 0x8525, 0x89AC, 0x289A, 0x8367, 0x8478, 0x4647}, + {0x1503, 0x4514, 0x2412, 0x289A, 0x199B, 0x8367, 0x8478, 0x4647}, + {0x2301, 0x2412, 0x4514, 0x8478, 0x8367, 0x4647}, + {0x2301, 0x1503, 0x199B, 0x289A, 0x8478, 0x8367, 0x4647}, + {0x8478, 0x8367, 0x4647}, + {0x1503, 0x4514, 0x4445, 0x8478, 0x88BC, 0x199B}, + {0x2301, 0x4514, 0x4445, 0x8478, 0x88BC, 0x289A}, + {0x1503, 0x2301, 0x2412, 0x4445, 0x8478, 0x88BC, 0x199B}, + {0x8478, 0x4445, 0x2412, 0x289A, 0x88BC}, + {0x1503, 0x4514, 0x4445, 0x8478, 0x88BC, 0x199B, 0x8525, 0x2412, 0x289A, 0x89AC}, + {0x8525, 0x2412, 0x2301, 0x4514, 0x4445, 0x8478, 0x88BC, 0x89AC}, + {0x8525, 0x4445, 0x8478, 0x1503, 0x2301, 0x88BC, 0x199B, 0x289A, 0x89AC}, + {0x8478, 0x4445, 0x8525, 0x89AC, 0x88BC}, + {0x8478, 0x8658, 0x8525, 0x4514, 0x1503, 0x199B, 0x88BC}, + {0x2301, 0x4514, 0x8525, 0x8658, 0x8478, 0x88BC, 0x289A}, + {0x8478, 0x8658, 0x8525, 0x2412, 0x2301, 0x1503, 0x199B, 0x88BC}, + {0x8478, 0x8658, 0x8525, 0x2412, 0x289A, 0x88BC}, + {0x2412, 0x4514, 0x1503, 0x8478, 0x8658, 0x199B, 0x88BC, 0x89AC, 0x289A}, + {0x8478, 0x8658, 0x89AC, 0x88BC, 0x2301, 0x2412, 0x4514}, + {0x1503, 0x2301, 0x8658, 0x8478, 0x289A, 0x89AC, 0x88BC, 0x199B}, + {0x8478, 0x8658, 0x89AC, 0x88BC}, + {0x8658, 0x4445, 0x4514, 0x1503, 0x199B, 0x89AC}, + {0x8658, 0x4445, 0x4514, 0x2301, 0x289A, 0x89AC}, + {0x8658, 0x4445, 0x2412, 0x2301, 0x1503, 0x199B, 0x89AC}, + {0x2412, 0x4445, 0x8658, 0x89AC, 0x289A}, + {0x2412, 0x8525, 0x8658, 0x4445, 0x4514, 0x1503, 0x199B, 0x289A}, + {0x8525, 0x2412, 0x2301, 0x4514, 0x4445, 0x8658}, + {0x1503, 0x2301, 0x289A, 0x199B, 0x8658, 0x8525, 0x4445}, + {0x8525, 0x8658, 0x4445}, + {0x8525, 0x4514, 0x1503, 0x199B, 0x89AC}, + {0x8525, 0x4514, 0x2301, 0x289A, 0x89AC}, + {0x8525, 0x2412, 0x2301, 0x1503, 0x199B, 0x89AC}, + {0x8525, 0x2412, 0x289A, 0x89AC}, + {0x1503, 0x4514, 0x2412, 0x289A, 0x199B}, + {0x2301, 0x2412, 0x4514}, + {0x2301, 0x1503, 0x199B, 0x289A}, + {} + }; + + + /** + * The RegularCellData structure holds information about the triangulation + * used for a single equivalence class in the modified Marching Cubes algorithm, + * described in Section 3.2. + */ + static class RegularCellData { + char geometryCounts; // High nibble is vertex count, low nibble is triangle count. + char[] vertexIndex; // Groups of 3 indexes giving the triangulation. + + long getVertexCount(){ + return (geometryCounts >> 4); + } + + long getTriangleCount(){ + return (geometryCounts & 0x0F); + } + + public RegularCellData(char geometryCounts, char[] vertexIndex){ + this.geometryCounts = geometryCounts; + this.vertexIndex = vertexIndex; + } + + }; + + /** + * The TransitionCellData structure holds information about the triangulation + * used for a single equivalence class in the Transvoxel Algorithm transition cell, + * described in Section 4.3. + */ + static class TransitionCellData { + long geometryCounts; // High nibble is vertex count, low nibble is triangle count. + char[] vertexIndex; // Groups of 3 indexes giving the triangulation. + + long getVertexCount(){ + return (geometryCounts >> 4); + } + + long getTriangleCount(){ + return (geometryCounts & 0x0F); + } + + public TransitionCellData(char geometryCounts, char[] vertexIndex){ + this.geometryCounts = geometryCounts; + this.vertexIndex = vertexIndex; + } + }; } diff --git a/src/main/java/electrosphere/server/content/ServerContentManager.java b/src/main/java/electrosphere/server/content/ServerContentManager.java index 1fc2e05d..6a1f3847 100644 --- a/src/main/java/electrosphere/server/content/ServerContentManager.java +++ b/src/main/java/electrosphere/server/content/ServerContentManager.java @@ -7,14 +7,12 @@ import org.joml.Vector3i; import electrosphere.engine.Globals; import electrosphere.entity.Entity; import electrosphere.entity.EntityUtils; -import electrosphere.entity.ServerEntityUtils; import electrosphere.entity.types.creature.CreatureUtils; import electrosphere.entity.types.item.ItemUtils; import electrosphere.server.content.serialization.ContentSerialization; import electrosphere.server.content.serialization.EntitySerialization; import electrosphere.server.datacell.Realm; import electrosphere.server.datacell.ServerDataCell; -import electrosphere.server.pathfinding.NavMeshUtils; import electrosphere.server.saves.SaveUtils; import electrosphere.util.FileUtils; @@ -54,7 +52,8 @@ public class ServerContentManager { hydrateRawContent(realm,cell,contentRaw); } else { //else create from scratch - EnvironmentGenerator.generateForest(realm, cell, worldPos, 0); + //UNCOMMENT THIS WHEN YOU WANT CONTENT GENERATED FOR WORLDS AGAIN + // EnvironmentGenerator.generateForest(realm, cell, worldPos, 0); } } else { //just because content wasn't generated doesn't mean there isn't data saved under that key