diff --git a/pom.xml b/pom.xml index 890b24d9..786ec3db 100644 --- a/pom.xml +++ b/pom.xml @@ -100,14 +100,6 @@ commons-crypto 1.1.0 - - - - - electrosphere - TerrainGen - 0.1 - diff --git a/src/main/java/electrosphere/game/state/LoadingThread.java b/src/main/java/electrosphere/game/state/LoadingThread.java index 832a6343..e8b1d730 100644 --- a/src/main/java/electrosphere/game/state/LoadingThread.java +++ b/src/main/java/electrosphere/game/state/LoadingThread.java @@ -382,7 +382,7 @@ public class LoadingThread extends Thread { // } - for(int i = 0; i < 50; i++){ + for(int i = 0; i < 10; i++){ Random rand = new Random(); String treePath = "Models/tree1.fbx"; Entity tree = EntityUtils.spawnDrawableEntity(treePath); diff --git a/src/main/java/electrosphere/game/terrain/TerrainManager.java b/src/main/java/electrosphere/game/terrain/TerrainManager.java index f7c09fbf..a86edada 100644 --- a/src/main/java/electrosphere/game/terrain/TerrainManager.java +++ b/src/main/java/electrosphere/game/terrain/TerrainManager.java @@ -2,8 +2,8 @@ package electrosphere.game.terrain; import com.google.gson.Gson; import electrosphere.main.Globals; -import electrosphere.terraingen.TerrainGen; -import electrosphere.terraingen.models.TerrainModel; +import electrosphere.game.terrain.generation.TerrainGen; +import electrosphere.game.terrain.generation.models.TerrainModel; import electrosphere.util.Utilities; import java.io.File; import java.io.IOException; diff --git a/src/main/java/electrosphere/game/terrain/generation/Continent.java b/src/main/java/electrosphere/game/terrain/generation/Continent.java new file mode 100644 index 00000000..80d9e07b --- /dev/null +++ b/src/main/java/electrosphere/game/terrain/generation/Continent.java @@ -0,0 +1,17 @@ +package electrosphere.game.terrain.generation; + +/** + * + * @author satellite + */ +class Continent { + public int dim_x; + public int dim_y; + public int size = 0; + public int[][] elevation; + public int[][] chart_Precipitation; + public int[][] chart_Temperature; + public int[][] chart_Climate; + public int[][] chart_Wind_Macro; + public Region[][] regions; +} diff --git a/src/main/java/electrosphere/game/terrain/generation/Hotspot.java b/src/main/java/electrosphere/game/terrain/generation/Hotspot.java new file mode 100644 index 00000000..6fb58aa5 --- /dev/null +++ b/src/main/java/electrosphere/game/terrain/generation/Hotspot.java @@ -0,0 +1,144 @@ +package electrosphere.game.terrain.generation; + +/** + * + * @author satellite + */ +class Hotspot { + + int x; + int y; + int life_current; + int life_max; + int magnitude_current; + int magnitude_max; + TerrainGenerator parent; + + + public Hotspot(int x, int y, int life_max, int magnitude, TerrainGenerator parent) { + this.x = x; + this.y = y; + this.life_current = 0; + this.life_max = life_max; + this.magnitude_current = 1; + this.magnitude_max = magnitude; + this.parent = parent; + } + + public void simulate() { + if ((1.0f - (Math.abs((life_max / 2) - life_current) / (life_max / 2))) > 0.8f) { + magnitude_current = magnitude_max; + } else if ((1.0f - (Math.abs((life_max / 2) - life_current) / (life_max / 2))) > 0.6f) { + magnitude_current = (int) (0.8f * magnitude_max); + } else if ((1.0f - (Math.abs((life_max / 2) - life_current) / (life_max / 2))) > 0.4f) { + magnitude_current = (int) (0.6f * magnitude_max); + } else if ((1.0f - (Math.abs((life_max / 2) - life_current) / (life_max / 2))) > 0.2f) { + magnitude_current = (int) (0.4f * magnitude_max); + } else { + magnitude_current = (int) (0.2f * magnitude_max); + } + //affect asthenosphere heat + if (magnitude_current == 1) { + parent.asthenosphere_Heat[x][y] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + } else if (magnitude_current == 2) { + parent.asthenosphere_Heat[x][y] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + if (x + 1 < parent.DIMENSION) { + parent.asthenosphere_Heat[x + 1][y] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + } + if (x - 1 >= 0) { + parent.asthenosphere_Heat[x - 1][y] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + } + if (y + 1 < parent.DIMENSION) { + parent.asthenosphere_Heat[x][y + 1] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + } + if (y - 1 >= 0) { + parent.asthenosphere_Heat[x][y - 1] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + } + } else if (magnitude_current == 3) { + parent.asthenosphere_Heat[x][y] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + if (x + 1 < parent.DIMENSION) { + parent.asthenosphere_Heat[x + 1][y] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + } + if (x - 1 >= 0) { + parent.asthenosphere_Heat[x - 1][y] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + } + if (y + 1 < parent.DIMENSION) { + parent.asthenosphere_Heat[x][y + 1] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + if (x + 1 < parent.DIMENSION) { + parent.asthenosphere_Heat[x + 1][y + 1] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + } + if (x - 1 >= 0) { + parent.asthenosphere_Heat[x - 1][y + 1] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + } + } + if (y - 1 >= 0) { + parent.asthenosphere_Heat[x][y - 1] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + if (x + 1 < parent.DIMENSION) { + parent.asthenosphere_Heat[x + 1][y - 1] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + } + if (x - 1 >= 0) { + parent.asthenosphere_Heat[x - 1][y - 1] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + } + } + } else if (magnitude_current == 4) { + parent.asthenosphere_Heat[x][y] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + if (x + 1 < parent.DIMENSION) { + parent.asthenosphere_Heat[x + 1][y] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + } + if (x - 1 >= 0) { + parent.asthenosphere_Heat[x - 1][y] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + } + if (y + 1 < parent.DIMENSION) { + parent.asthenosphere_Heat[x][y + 1] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + if (x + 1 < parent.DIMENSION) { + parent.asthenosphere_Heat[x + 1][y + 1] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + } + if (x - 1 >= 0) { + parent.asthenosphere_Heat[x - 1][y + 1] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + } + } + if (y - 1 >= 0) { + parent.asthenosphere_Heat[x][y - 1] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + if (x + 1 < parent.DIMENSION) { + parent.asthenosphere_Heat[x + 1][y - 1] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + } + if (x - 1 >= 0) { + parent.asthenosphere_Heat[x - 1][y - 1] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + } + } + } else if (magnitude_current == 5) { + parent.asthenosphere_Heat[x][y] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + if (x + 1 < parent.DIMENSION) { + parent.asthenosphere_Heat[x + 1][y] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + } + if (x - 1 >= 0) { + parent.asthenosphere_Heat[x - 1][y] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + } + if (y + 1 < parent.DIMENSION) { + parent.asthenosphere_Heat[x][y + 1] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + if (x + 1 < parent.DIMENSION) { + parent.asthenosphere_Heat[x + 1][y + 1] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + } + if (x - 1 >= 0) { + parent.asthenosphere_Heat[x - 1][y + 1] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + } + } + if (y - 1 >= 0) { + parent.asthenosphere_Heat[x][y - 1] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + if (x + 1 < parent.DIMENSION) { + parent.asthenosphere_Heat[x + 1][y - 1] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + } + if (x - 1 >= 0) { + parent.asthenosphere_Heat[x - 1][y - 1] = (int) (100.0f - Math.abs((life_max / 2) - life_current) * 100 / (life_max / 2)); + } + } + } + life_current++; + } + public void add_to_elevation(int x, int y, int magnitude){ + parent.elevation[x][y] = parent.elevation[x][y] + magnitude; + if(parent.elevation[x][y] > 100){ + parent.elevation[x][y] = 100; + } + } +} diff --git a/src/main/java/electrosphere/game/terrain/generation/InterpolationDisplay.java b/src/main/java/electrosphere/game/terrain/generation/InterpolationDisplay.java new file mode 100644 index 00000000..f69b9a3b --- /dev/null +++ b/src/main/java/electrosphere/game/terrain/generation/InterpolationDisplay.java @@ -0,0 +1,218 @@ +package electrosphere.game.terrain.generation; + +import java.awt.Color; +import java.awt.Graphics; +import javax.swing.JPanel; + +/** + * + * @author satellite + */ +class InterpolationDisplay extends JPanel{ + @Override + public void paint(Graphics g) { + if(TerrainGen.display_toggle == 0) { + for (int x = 0; x < TerrainGen.DIMENSION; x++) { + for (int y = 0; y < TerrainGen.DIMENSION; y++) { + if (TerrainGen.mountain_parsed[x][y] > TerrainGen.mountain_Threshold - 1) { + g.setColor(new Color((int) (TerrainGen.elevation[x][y] / 100.0 * 254 * (TerrainGen.brightness / 100.0)), 1, 1)); + } else if (TerrainGen.ocean_parsed[x][y] > TerrainGen.ocean_Threshold - 1) { + g.setColor( + new Color( + 1, + (int) ((TerrainGen.elevation[x][y] + 50) / 100.0 * 254 * (TerrainGen.brightness / 100.0)), + (int) ((TerrainGen.elevation[x][y] + 50) / 100.0 * 254 * (TerrainGen.brightness / 100.0)) + ) + ); + } else { + g.setColor(new Color(1, (int) (TerrainGen.elevation[x][y] / 100.0 * 254 * (TerrainGen.brightness / 100.0)), 1)); + } + g.fillRect(x * 2 + 25, y * 2 + 25, 2, 2); + } + } + } else if(TerrainGen.display_toggle == 1){ + for (int x = 0; x < TerrainGen.DIMENSION; x++) { + for (int y = 0; y < TerrainGen.DIMENSION; y++) { + if (TerrainGen.precipitation_Chart[x][y] > 0) { + g.setColor( + new Color( + 1, + (int) ((TerrainGen.elevation[x][y]) / 100.0 * 254 * (TerrainGen.brightness / 100.0)), + (int) ((TerrainGen.elevation[x][y]) / 100.0 * 254 * (TerrainGen.brightness / 100.0)) + ) + ); + } else { + g.setColor(new Color((int) (TerrainGen.elevation[x][y] / 100.0 * 254 * (TerrainGen.brightness / 100.0)), 1, 1)); + } + g.fillRect(x * 2 + 25, y * 2 + 25, 2, 2); + } + } + } else if(TerrainGen.display_toggle == 2){ + for (int x = 0; x < TerrainGen.DIMENSION; x++) { + for (int y = 0; y < TerrainGen.DIMENSION; y++) { +// if (TerrainInterpolator.precipitation_Chart[x][y] > 0) { + g.setColor( + new Color( + (int) ((TerrainGen.temperature_Chart[x][y]) / 100.0 * 254 * (TerrainGen.brightness / 100.0)), + (int) ((TerrainGen.elevation[x][y]) / 100.0 * 254 * (TerrainGen.brightness / 100.0)), + 1 + ) + ); +// } else { +// g.setColor(new Color((int) (TerrainInterpolator.elevation[x][y] / 100.0 * 254 * (TerrainInterpolator.brightness / 100.0)), 1, 1)); +// } + g.fillRect(x * 2 + 25, y * 2 + 25, 2, 2); + } + } + } else if(TerrainGen.display_toggle == 3){ + for (int x = 0; x < TerrainGen.DIMENSION; x++) { + for (int y = 0; y < TerrainGen.DIMENSION; y++) { + if (TerrainGen.climate_category[x][y] == 0) { + g.setColor(Color.BLUE); +// g.setColor( +// new Color( +// 1, +// (int) ((TerrainInterpolator.elevation[x][y]) / 100.0 * 254 * (TerrainInterpolator.brightness / 100.0)), +// (int) ((TerrainInterpolator.elevation[x][y]) / 100.0 * 254 * (TerrainInterpolator.brightness / 100.0)) +// ) +// ); + } else if(TerrainGen.climate_category[x][y] == 1){ + g.setColor(Color.RED); +// g.setColor( +// new Color( +// 1, +// (int) ((TerrainInterpolator.elevation[x][y]) / 100.0 * 254 * (TerrainInterpolator.brightness / 100.0)), +// 1 +// ) +// ); + } else if(TerrainGen.climate_category[x][y] == 2){ + g.setColor(Color.GREEN); +// g.setColor( +// new Color( +// 1, +// (int) ((TerrainInterpolator.elevation[x][y]) / 100.0 * 254 * (TerrainInterpolator.brightness / 100.0)), +// 1 +// ) +// ); + } else if(TerrainGen.climate_category[x][y] == 3){ + g.setColor(Color.YELLOW); +// g.setColor( +// new Color( +// 1, +// 1, +// (int) ((TerrainInterpolator.elevation[x][y]) / 100.0 * 254 * (TerrainInterpolator.brightness / 100.0)) +// ) +// ); + } else if(TerrainGen.climate_category[x][y] == 4){ + g.setColor( + new Color( + (int) ((TerrainGen.elevation[x][y]) / 100.0 * 254 * (TerrainGen.brightness / 100.0)), + (int) ((TerrainGen.elevation[x][y]) / 100.0 * 254 * (TerrainGen.brightness / 100.0)), + 1 + ) + ); + g.setColor(Color.ORANGE); + } else if(TerrainGen.climate_category[x][y] == 5){ + g.setColor( + new Color( + (int) ((TerrainGen.elevation[x][y]) / 100.0 * 254 * (TerrainGen.brightness / 100.0)), + 1, + (int) ((TerrainGen.elevation[x][y]) / 100.0 * 254 * (TerrainGen.brightness / 100.0)) + ) + ); + g.setColor(Color.BLACK); + } else if(TerrainGen.climate_category[x][y] == 6){ + g.setColor( + new Color( + (int) ((TerrainGen.elevation[x][y]) / 100.0 * 254 * (TerrainGen.brightness / 100.0)), + (int) ((TerrainGen.elevation[x][y]) / 100.0 * 254 * (TerrainGen.brightness / 100.0)), + (int) ((TerrainGen.elevation[x][y]) / 100.0 * 254 * (TerrainGen.brightness / 100.0)) + ) + ); + } else { + g.setColor(new Color((int) (TerrainGen.elevation[x][y] / 100.0 * 254 * (TerrainGen.brightness / 100.0)), 1, 1)); + } + g.fillRect(x * 2 + 25, y * 2 + 25, 2, 2); + } + } + } else if(TerrainGen.display_toggle == 4){ + for (int x = 0; x < TerrainGen.DIMENSION; x++) { + for (int y = 0; y < TerrainGen.DIMENSION; y++) { + if (TerrainGen.continent_Number[x][y] > 8) { + g.setColor(Color.PINK); + } else if (TerrainGen.continent_Number[x][y] > 7) { + g.setColor(Color.DARK_GRAY); + } else if (TerrainGen.continent_Number[x][y] > 6) { + g.setColor(Color.CYAN); + } else if (TerrainGen.continent_Number[x][y] > 5) { + g.setColor(Color.GRAY); + } else if (TerrainGen.continent_Number[x][y] > 4) { + g.setColor(Color.orange); + } else if (TerrainGen.continent_Number[x][y] > 3) { + g.setColor(Color.green); + } else if (TerrainGen.continent_Number[x][y] > 2) { + g.setColor(Color.yellow); + } else if (TerrainGen.continent_Number[x][y] > 1) { + g.setColor(Color.blue); + } else if (TerrainGen.continent_Number[x][y] > 0) { + g.setColor(Color.red); + } else { + g.setColor(Color.BLACK); + } + g.fillRect(x * 2 + 25, y * 2 + 25, 2, 2); + } + } + } else if (TerrainGen.display_toggle == 5) { + Continent current = TerrainGen.continents.get(TerrainGen.current_Continent); + g.drawString("dim_x: " + current.dim_x, 20, 20); + g.drawString("dim_y: " + current.dim_y, 20, 30); + for (int x = 0; x < current.dim_x; x++) { + for (int y = 0; y < current.dim_y; y++) { + if (current.elevation[x][y] > 10) { + g.setColor(new Color(1, (int) (current.elevation[x][y] / 100.0 * 254 * (TerrainGen.brightness / 100.0)), 1)); + g.fillRect(50 + x * 2, 50 + y * 2, 2, 2); + } + } + } + } else if (TerrainGen.display_toggle == 6){ + Continent current = TerrainGen.continents.get(TerrainGen.current_Continent); + for(int x = 0; x < Region.REGION_DIMENSION; x++){ + for(int y = 0; y < Region.REGION_DIMENSION; y++){ + if(current.regions[TerrainGen.current_Region_X][TerrainGen.current_Region_Y].chart_Drainage[x][y] > 0){ + g.setColor(Color.BLUE); + } else { + g.setColor(Color.BLACK); + } + g.fillRect(50 + x * 2, 50 + y * 2, 2, 2); + } + } + } +// if(TerrainInterpolator.display_toggle == 0) { +// g.drawString("Elevation Raws", 10, 10); +// for (int x = 0; x < 100; x++) { +// for (int y = 0; y < 100; y++) { +// g.setColor(new Color(1, (int) (TerrainInterpolator.elevation[x][y] / 100.0 * 254 * (TerrainInterpolator.brightness / 100.0)), 1)); +// g.fillRect(x * 2 + 25, y * 2 + 25, 2, 2); +// } +// } +// } else if(TerrainInterpolator.display_toggle == 1) { +// g.drawString("Parsed Mountains", 10, 10); +// for (int x = 0; x < 100; x++) { +// for (int y = 0; y < 100; y++) { +// if(TerrainInterpolator.mountain_parsed[x][y] > TerrainInterpolator.mountain_Threshold - 1){ +// g.setColor(new Color((int) (TerrainInterpolator.elevation[x][y] / 100.0 * 254 * (TerrainInterpolator.brightness / 100.0)), 1, 1)); +// } else { +// g.setColor(new Color(1, (int) (TerrainInterpolator.elevation[x][y] / 100.0 * 254 * (TerrainInterpolator.brightness / 100.0)), 1)); +// } +// g.fillRect(x * 2 + 25, y * 2 + 25, 2, 2); +// } +// } +// } else if(TerrainInterpolator.display_toggle == 2){ +// g.drawString("Oceans", 10, 10); +// } else if(TerrainInterpolator.display_toggle == 3){ +// g.drawString("Continents", 10, 10); +// } else if(TerrainInterpolator.display_toggle == 4){ +// g.drawString("Temperatures", 10, 10); +// } + } +} diff --git a/src/main/java/electrosphere/game/terrain/generation/MidoceanicRidge.java b/src/main/java/electrosphere/game/terrain/generation/MidoceanicRidge.java new file mode 100644 index 00000000..48391107 --- /dev/null +++ b/src/main/java/electrosphere/game/terrain/generation/MidoceanicRidge.java @@ -0,0 +1,9 @@ +package electrosphere.game.terrain.generation; + +/** + * + * @author satellite + */ +class MidoceanicRidge { + +} diff --git a/src/main/java/electrosphere/game/terrain/generation/Region.java b/src/main/java/electrosphere/game/terrain/generation/Region.java new file mode 100644 index 00000000..ee4d7f2a --- /dev/null +++ b/src/main/java/electrosphere/game/terrain/generation/Region.java @@ -0,0 +1,19 @@ +package electrosphere.game.terrain.generation; + +/** + * + * @author satellite + */ +class Region { + public static int REGION_DIMENSION = 100; + int chart_Elevation[][]; + int chart_Drainage[][]; + int chart_Max_Water_Flow[][]; + int elevation_Goal; + Region neighbors[][]; + public boolean finished_Drainage_Simulation = false; + public Region(){ + neighbors = new Region[3][3]; + neighbors[1][1] = null; + } +} diff --git a/src/main/java/electrosphere/game/terrain/generation/TerrainGen.java b/src/main/java/electrosphere/game/terrain/generation/TerrainGen.java new file mode 100644 index 00000000..f06b5fd5 --- /dev/null +++ b/src/main/java/electrosphere/game/terrain/generation/TerrainGen.java @@ -0,0 +1,1416 @@ +package electrosphere.game.terrain.generation; + +import electrosphere.game.terrain.generation.models.TerrainModel; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Properties; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JFrame; + +/** + * + * @author satellite + */ +public class TerrainGen { + public static int elevation[][]; + public static int DIMENSION = 100; + //the interpolation ratio applied to the statically generated terrain + static int interpolationRatio = 10; + //vertical static interpolation ratio + static int verticalInterpolationRatio = 10; + //the interpolation ratio applied to the dynamically generated terrain per chunk + static int dynamicInterpRatio = 1000; + static JFrame frame = new JFrame(); + static InterpolationDisplay graphics = new InterpolationDisplay(); + public static int brightness = 0; + static int brightness_hold_incrementer = 0; + static boolean brightness_increasing = true; + public static int display_toggle = 0; + static final int max_Display_Toggle = 6; + + public static int mountain_parsed[][]; + static final int mountain_Threshold = 75; + static final int mountain_Range_Size_Minimum = 10; + + public static int ocean_parsed[][]; + static final int ocean_Threshold = 25; + + static Vector wind_field[][]; + + public static int[][] precipitation_Chart; + static final int rain_Shadow_Blocker_Height = 80; + + public static int[][] temperature_Chart; + + public static long[][] chunkRandomizer; + + /* + 0 - ocean + 1 - tropical + 2 - subtropical + 3 - temperate + 4 - dry + 5 - polar + 6 - mountainous + */ + public static int[][] climate_category; + + public static int num_Continents = 0; + public static int[][] continent_Number; + + public static ArrayList continents = new ArrayList(); + + public static int current_Continent = 1; + public static int current_Region_X = 0; + public static int current_Region_Y = 0; + + public static void main(String args[]){ + + TerrainGenerator tergen = new TerrainGenerator(); + tergen.set_Dimension(DIMENSION); + tergen.set_Lifespan(15000); + tergen.run(); + elevation = tergen.get_Terrain(); + + + elevation = interpolate_Elevation_Raws(elevation); + DIMENSION = DIMENSION * 2; + + elevation = compression_filter(elevation); + elevation = three_halves_filter(elevation); + elevation = smooth_Terrain_Further(elevation); + elevation = small_Kernel_Sharpen(elevation); + elevation = smooth_Terrain_Further(elevation); + elevation = three_halves_filter(elevation); + elevation = small_Kernel_Smooth(elevation); + elevation = small_Kernel_Sharpen(elevation); + elevation = smooth_Terrain_Further(elevation); + elevation = smooth_Terrain_Further(elevation); + elevation = land_exponential_filter(elevation); + elevation = land_exponential_filter(elevation); + elevation = land_exponential_filter(elevation); + elevation = land_exponential_filter(elevation); + + mountain_parsed = new int[DIMENSION][DIMENSION]; + ocean_parsed = new int[DIMENSION][DIMENSION]; + continent_Number = new int[DIMENSION][DIMENSION]; + + mountain_parsed = parse_Mountainscapes(elevation); + + ocean_parsed = parse_Oceans(elevation); + + wind_field = map_Wind_Field(); + + precipitation_Chart = calculate_Rain_Shadows(elevation,ocean_parsed,wind_field); + + temperature_Chart = generate_Temperature_Chart(); + + climate_category = infer_Climate_Category(); + + determine_Continents(); + + fill_Continents(); + + //for display tbh + anchor_To_Real_Region(); + + + display_toggle = 0; + + create_Frame(); + + while(true){ + if(brightness_increasing){ + if(brightness < 100){ + brightness++; + } else { + } + } else { + if(brightness > 0){ + brightness--; + } else { + brightness_increasing = true; + display_toggle++; + if(display_toggle > 1){ + display_toggle = 0; + } + } + } + frame.repaint(); + Utilities.sleep(10); + } + } + + public TerrainModel generateModel(){ + TerrainModel rVal; + + TerrainGenerator tergen = new TerrainGenerator(); + tergen.set_Dimension(DIMENSION); + tergen.set_Lifespan(15000); + tergen.run(); + elevation = tergen.get_Terrain(); + + + elevation = interpolate_Elevation_Raws(elevation); + DIMENSION = DIMENSION * 2; + + elevation = compression_filter(elevation); + elevation = three_halves_filter(elevation); + elevation = smooth_Terrain_Further(elevation); + elevation = small_Kernel_Sharpen(elevation); + elevation = smooth_Terrain_Further(elevation); + elevation = three_halves_filter(elevation); + elevation = small_Kernel_Smooth(elevation); + elevation = small_Kernel_Sharpen(elevation); + elevation = smooth_Terrain_Further(elevation); + elevation = smooth_Terrain_Further(elevation); + elevation = land_exponential_filter(elevation); + elevation = land_exponential_filter(elevation); + elevation = land_exponential_filter(elevation); + elevation = land_exponential_filter(elevation); + + mountain_parsed = new int[DIMENSION][DIMENSION]; + ocean_parsed = new int[DIMENSION][DIMENSION]; + continent_Number = new int[DIMENSION][DIMENSION]; + + mountain_parsed = parse_Mountainscapes(elevation); + + ocean_parsed = parse_Oceans(elevation); + + wind_field = map_Wind_Field(); + + precipitation_Chart = calculate_Rain_Shadows(elevation,ocean_parsed,wind_field); + + temperature_Chart = generate_Temperature_Chart(); + + climate_category = infer_Climate_Category(); + + determine_Continents(); + + fill_Continents(); + + float[][] modelInput = interpolateIntegerElevations(elevation); + + modelInput = applyRandomizationFilter(modelInput); + + chunkRandomizer = getChunkRandomizer(modelInput, DIMENSION); + + rVal = new TerrainModel(DIMENSION,modelInput,chunkRandomizer,dynamicInterpRatio); + + return rVal; + } + + public long[][] getChunkRandomizer(float[][] elevationMap, int DIMENSION){ + long[][] rVal = new long[DIMENSION][DIMENSION]; + for(int x = 0; x < DIMENSION; x++){ + for(int y = 0; y < DIMENSION; y++){ + long seed = (long)elevationMap[x][y]; + if(x - 1 > 0){ + seed = seed + (long)elevationMap[x-1][y]; + if(y - 1 > 0){ + seed = seed + (long)elevationMap[x-1][y-1]; + } + if(y + 1 < DIMENSION){ + seed = seed + (long)elevationMap[x-1][y+1]; + } + } + if(x + 1 < DIMENSION){ + seed = seed + (long)elevationMap[x+1][y]; + if(y - 1 > 0){ + seed = seed + (long)elevationMap[x+1][y-1]; + } + if(y + 1 < DIMENSION){ + seed = seed + (long)elevationMap[x+1][y+1]; + } + } + if(y - 1 > 0){ + seed = seed + (long)elevationMap[x][y-1]; + } + if(y + 1 < DIMENSION){ + seed = seed + (long)elevationMap[x][y+1]; + } + rVal[x][y] = seed; + } + } + return rVal; + } + + static float[][] applyRandomizationFilter(float[][] elevation){ + float[][] rVal = new float[elevation.length][elevation[0].length]; + for(int x = 0; x < DIMENSION-1; x++){ + for(int y = 0; y < DIMENSION-1; y++){ + rVal[x][y] = elevation[x][y] + Utilities.random_Integer(0, (int)(elevation[x][y] / 4)); + } + } + return rVal; + } + + static float[][] interpolateIntegerElevations(int[][] elevation){ + int interpRatio = interpolationRatio; + float[][] rVal = new float[DIMENSION * interpRatio][DIMENSION * interpRatio]; + for(int x = 0; x < DIMENSION-1; x++){ + for(int y = 0; y < DIMENSION-1; y++){ + for(int i = 0; i < interpRatio; i++){ + for(int j = 0; j < interpRatio; j++){ + float ratio1 = (i*j) * 1.0f / (interpRatio*interpRatio); + float ratio2 = ((interpRatio-i)*j) * 1.0f / (interpRatio*interpRatio); + float ratio3 = (i*(interpRatio-j)) * 1.0f / (interpRatio*interpRatio); + float ratio4 = ((interpRatio-i)*(interpRatio-j)) * 1.0f / (interpRatio*interpRatio); + rVal[x*interpRatio+interpRatio-i][y*interpRatio+interpRatio-j] = + (elevation[x ][y ] * ratio1 + + elevation[x+1][y ] * ratio2 + + elevation[x ][y+1] * ratio3 + + elevation[x+1][y+1] * ratio4) * + verticalInterpolationRatio + ; + } + } + } + } + DIMENSION = DIMENSION * interpRatio; + return rVal; + } + + public static void anchor_To_Real_Region(){ + //current_Region_X + //continents + //current_Continent + Continent target_continent = continents.get(current_Continent); + for(int x = 0; x < target_continent.dim_x; x++){ + for(int y = 0; y < target_continent.dim_y; y++){ + if(target_continent.regions[x][y]!=null){ + current_Region_X = x; + current_Region_Y = y; + x = target_continent.dim_x; + y = target_continent.dim_y; + } else { + + } + } + } + } + + static int[][] simulate_Drainage(int[][] water_level, int[][] elevation, int dim_x, int dim_y){ + int[][] rVal = new int[dim_x][dim_y]; + for(int x = 0; x < dim_x; x++){ + for(int y = 0; y < dim_y; y++){ + rVal[x][y] = 0; + } + } + int kernel_offset_x[] = { + -1,0,0,1 + }; + int kernel_offset_y[] = { + 0,-1,1,0 + }; + int offset_kernel_size = 4; + for(int x = 0; x < dim_x; x++){ + for(int y = 0; y < dim_y; y++){ + int num_Water_Particles = water_level[x][y]; + int elevation_Center = elevation[x][y]; + int attractor_Values[] = new int[offset_kernel_size]; + for(int j = 0; j < offset_kernel_size; j++) { + if(x + kernel_offset_x[j] >= 0 && x + kernel_offset_x[j] < dim_x && y + kernel_offset_y[j] >= 0 && y + kernel_offset_y[j] < dim_y) { + if(elevation[x+kernel_offset_x[j]][y+kernel_offset_y[j]] < elevation[x][y]){ + attractor_Values[j] = (int)((elevation[x][y]-elevation[x+kernel_offset_x[j]][y+kernel_offset_y[j]])*1.0*water_level[x+kernel_offset_x[j]][y+kernel_offset_y[j]]); + } else { + attractor_Values[j] = 0; + } + } + } + int rand_num = 0; + int rand_Space = 0; + for(int j = 0; j < offset_kernel_size; j++){ + rand_Space = rand_Space + attractor_Values[j]; + } + rand_Space = rand_Space + water_level[x][y]; // account for water not moving downwards + int rand_Incrementer = 0; + boolean done = false; + for(int i = 0; i < num_Water_Particles; i++){ + done = false; + rand_Incrementer = 0; + rand_num = Utilities.random_Integer(0, rand_Space); + for(int j = 0; j < offset_kernel_size; j++){ + rand_Incrementer = rand_Incrementer + attractor_Values[j]; + if(rand_num < rand_Incrementer){ + if(attractor_Values[j] > 0){ + if(rVal[x+kernel_offset_x[j]][y+kernel_offset_y[j]] < 400){ + rVal[x+kernel_offset_x[j]][y+kernel_offset_y[j]]++; + } + done = true; + } + } + } + if(!done){ + rVal[x][y]++; + } + } + } + } + return rVal; + } + + static int sum_Array(int[][] arr, int dim_x, int dim_y){ + int sum = 0; + for(int x = 0; x < dim_x; x++){ + for(int y = 0; y < dim_y; y++){ + sum = sum + arr[x][y]; + } + } + return sum; + } + + static void generate_Region_Elevation_Maps(Continent c){ + int neighbor_offset_x[] = { + 1,0,-1,1,-1,1,0,-1 + }; + int neighbor_offset_y[] = { + 1,1,1,0,0,-1,-1,-1 + }; + //generate neighbor links + for(int x = 0; x < c.dim_x; x++){ + for(int y = 0; y < c.dim_y; y++){ + if(c.regions[x][y]!=null){ + for(int i = 0; i < 8; i++){ + if(x + neighbor_offset_x[i] >= 0 && x + neighbor_offset_x[i] < c.dim_x && y + neighbor_offset_y[i] >= 0 && y + neighbor_offset_y[i] < c.dim_y){ + //+1 comes from getting the center of the kernel + //ie kernel is 3x3 therefore the center position is at 1,1 array-wise + c.regions[x][y].neighbors[neighbor_offset_x[i]+1][neighbor_offset_y[i]+1] = c.regions[x + neighbor_offset_x[i]][y + neighbor_offset_y[i]]; + } else { + c.regions[x][y].neighbors[neighbor_offset_x[i]+1][neighbor_offset_y[i]+1] = null; + } + } + } + } + } + int region_Dim = Region.REGION_DIMENSION; + //set elevation goals + for(int x = 0; x < c.dim_x; x++){ + for(int y = 0; y < c.dim_y; y++){ + if(c.regions[x][y]!=null){ + c.regions[x][y].elevation_Goal = c.elevation[x][y]; + } + } + } + //interpolate base elevation values + int interpolation_kernel_offset_x[] = {0,1,0,1}; + int interpolation_kernel_offset_y[] = {0,0,1,1}; + int interpolation_kernel_size = 4; + int corner_Vals[][] = new int[2][2]; + for(int x = 0; x < c.dim_x; x++){ + for(int y = 0; y < c.dim_y; y++){ + if (c.regions[x][y] != null) { + c.regions[x][y].chart_Elevation = new int[Region.REGION_DIMENSION][Region.REGION_DIMENSION]; + //first the north-west corner + for(int i = 0; i < interpolation_kernel_size; i++){ + if(x-1+interpolation_kernel_offset_x[i] >= 0 && x-1+interpolation_kernel_offset_x[i] < c.dim_x && + y-1+interpolation_kernel_offset_y[i] >= 0 && y-1+interpolation_kernel_offset_y[i] < c.dim_y){ + if(c.regions[x-1+interpolation_kernel_offset_x[i]][y-1+interpolation_kernel_offset_y[i]]!=null){ + corner_Vals[interpolation_kernel_offset_x[i]][interpolation_kernel_offset_y[i]] = + c.regions[x-1+interpolation_kernel_offset_x[i]][y-1+interpolation_kernel_offset_y[i]].elevation_Goal; + } else { + corner_Vals[interpolation_kernel_offset_x[i]][interpolation_kernel_offset_y[i]] = 0; + } + } + } + int midpoint = (int)(Region.REGION_DIMENSION/2.0); + int sum = 0; + for(int i = 0; i < midpoint; i++){ + for(int j = 0; j < midpoint; j++){ + sum = 0; + sum = sum + (midpoint-i)*(midpoint-j)*corner_Vals[interpolation_kernel_offset_x[0]][interpolation_kernel_offset_y[0]]; + sum = sum + (i)*(midpoint-j)* corner_Vals[interpolation_kernel_offset_x[1]][interpolation_kernel_offset_y[0]]; + sum = sum + (midpoint-i)*(j)* corner_Vals[interpolation_kernel_offset_x[0]][interpolation_kernel_offset_y[1]]; + sum = sum + (i)*(j)* corner_Vals[interpolation_kernel_offset_x[1]][interpolation_kernel_offset_y[1]]; + c.regions[x][y].chart_Elevation[i][j] = sum; + } + } + //now the north-east corner + for(int i = 0; i < interpolation_kernel_size; i++){ + if(x+interpolation_kernel_offset_x[i] >= 0 && x+interpolation_kernel_offset_x[i] < c.dim_x && + y-1+interpolation_kernel_offset_y[i] >= 0 && y-1+interpolation_kernel_offset_y[i] < c.dim_y){ + if(c.regions[x+interpolation_kernel_offset_x[i]][y-1+interpolation_kernel_offset_y[i]]!=null){ + corner_Vals[interpolation_kernel_offset_x[i]][interpolation_kernel_offset_y[i]] = + c.regions[x+interpolation_kernel_offset_x[i]][y-1+interpolation_kernel_offset_y[i]].elevation_Goal; + } else { + corner_Vals[interpolation_kernel_offset_x[i]][interpolation_kernel_offset_y[i]] = 0; + } + } + } + for(int i = midpoint; i < Region.REGION_DIMENSION; i++){ + for(int j = 0; j < midpoint; j++){ + sum = 0; + sum = sum + (Region.REGION_DIMENSION-i)*(midpoint-j)*corner_Vals[interpolation_kernel_offset_x[0]][interpolation_kernel_offset_y[0]]; + sum = sum + (i-midpoint)*(midpoint-j)* corner_Vals[interpolation_kernel_offset_x[1]][interpolation_kernel_offset_y[0]]; + sum = sum + (Region.REGION_DIMENSION-i)*(j)* corner_Vals[interpolation_kernel_offset_x[0]][interpolation_kernel_offset_y[1]]; + sum = sum + (i-midpoint)*(j)* corner_Vals[interpolation_kernel_offset_x[1]][interpolation_kernel_offset_y[1]]; + c.regions[x][y].chart_Elevation[i][j] = sum; + } + } + //now the south-west corner + for(int i = 0; i < interpolation_kernel_size; i++){ + if(x-1+interpolation_kernel_offset_x[i] >= 0 && x-1+interpolation_kernel_offset_x[i] < c.dim_x && + y+interpolation_kernel_offset_y[i] >= 0 && y+interpolation_kernel_offset_y[i] < c.dim_y){ + if(c.regions[x-1+interpolation_kernel_offset_x[i]][y+interpolation_kernel_offset_y[i]]!=null){ + corner_Vals[interpolation_kernel_offset_x[i]][interpolation_kernel_offset_y[i]] = + c.regions[x-1+interpolation_kernel_offset_x[i]][y+interpolation_kernel_offset_y[i]].elevation_Goal; + } else { + corner_Vals[interpolation_kernel_offset_x[i]][interpolation_kernel_offset_y[i]] = 0; + } + } + } + for(int i = 0; i < midpoint; i++){ + for(int j = midpoint; j < Region.REGION_DIMENSION; j++){ + sum = 0; + sum = sum + (midpoint-i)*(Region.REGION_DIMENSION-j)*corner_Vals[interpolation_kernel_offset_x[0]][interpolation_kernel_offset_y[0]]; + sum = sum + (i)*(Region.REGION_DIMENSION-j)* corner_Vals[interpolation_kernel_offset_x[1]][interpolation_kernel_offset_y[0]]; + sum = sum + (midpoint-i)*(j-midpoint)* corner_Vals[interpolation_kernel_offset_x[0]][interpolation_kernel_offset_y[1]]; + sum = sum + (i)*(j-midpoint)* corner_Vals[interpolation_kernel_offset_x[1]][interpolation_kernel_offset_y[1]]; + c.regions[x][y].chart_Elevation[i][j] = sum; + } + } + //now the south-east corner + for(int i = 0; i < interpolation_kernel_size; i++){ + if(x+interpolation_kernel_offset_x[i] >= 0 && x+interpolation_kernel_offset_x[i] < c.dim_x && + y+interpolation_kernel_offset_y[i] >= 0 && y+interpolation_kernel_offset_y[i] < c.dim_y){ + if(c.regions[x+interpolation_kernel_offset_x[i]][y+interpolation_kernel_offset_y[i]]!=null){ + corner_Vals[interpolation_kernel_offset_x[i]][interpolation_kernel_offset_y[i]] = + c.regions[x+interpolation_kernel_offset_x[i]][y+interpolation_kernel_offset_y[i]].elevation_Goal; + } else { + corner_Vals[interpolation_kernel_offset_x[i]][interpolation_kernel_offset_y[i]] = 0; + } + } + } + for(int i = midpoint; i < Region.REGION_DIMENSION; i++){ + for(int j = midpoint; j < Region.REGION_DIMENSION; j++){ + sum = 0; + sum = sum + (Region.REGION_DIMENSION-i)*(Region.REGION_DIMENSION-j)*corner_Vals[interpolation_kernel_offset_x[0]][interpolation_kernel_offset_y[0]]; + sum = sum + (i-midpoint)*(Region.REGION_DIMENSION-j)* corner_Vals[interpolation_kernel_offset_x[1]][interpolation_kernel_offset_y[0]]; + sum = sum + (Region.REGION_DIMENSION-i)*(j-midpoint)* corner_Vals[interpolation_kernel_offset_x[0]][interpolation_kernel_offset_y[1]]; + sum = sum + (i-midpoint)*(j-midpoint)* corner_Vals[interpolation_kernel_offset_x[1]][interpolation_kernel_offset_y[1]]; + c.regions[x][y].chart_Elevation[i][j] = sum; + + } + } + } + } + } + Region region_Current = null; + for(int x = 0; x < c.dim_x; x++){ + for(int y = 0; y < c.dim_y; y++){ + if(c.regions[x][y]!=null){ + if(region_Current == null){ + region_Current = c.regions[x][y]; + } else if(c.regions[x][y].elevation_Goal > region_Current.elevation_Goal){ + region_Current = c.regions[x][y]; + } + } + } + } + //drainage simulation + //operates on an open set + int adjacency_array_x[] = { + 1,0,1,2 + }; + int adjacency_array_y[] = { + 0,1,2,1 + }; + boolean simulate_Drainage = true; + ArrayList open_Set = new ArrayList(); + open_Set.add(region_Current); + if(simulate_Drainage){ + while(!open_Set.isEmpty()){ + region_Current.chart_Drainage = new int[region_Dim][region_Dim]; + for(int x = 0; x < region_Dim; x++){ + for(int y = 0; y < region_Dim; y++){ + region_Current.chart_Drainage[x][y] = 1; + } + } + if (region_Current.neighbors[1][0] != null) { + if (region_Current.neighbors[1][0].finished_Drainage_Simulation == true) { + for(int x = 0; x < region_Dim; x++){ + region_Current.chart_Drainage[x][0] = 1 + region_Current.neighbors[1][0].chart_Drainage[x][region_Dim-1]; + } + } + } + if (region_Current.neighbors[0][1] != null) { + if (region_Current.neighbors[0][1].finished_Drainage_Simulation == true) { + for(int x = 0; x < region_Dim; x++){ + region_Current.chart_Drainage[0][x] = 1 + region_Current.neighbors[0][1].chart_Drainage[region_Dim-1][x]; + } + } + } + if (region_Current.neighbors[1][2] != null) { + if (region_Current.neighbors[1][2].finished_Drainage_Simulation == true) { + for(int x = 0; x < region_Dim; x++){ + region_Current.chart_Drainage[x][region_Dim-1] = 1 + region_Current.neighbors[1][2].chart_Drainage[x][0]; + } + } + } + if (region_Current.neighbors[2][1] != null) { + if (region_Current.neighbors[2][1].finished_Drainage_Simulation == true) { + for(int x = 0; x < region_Dim; x++){ + region_Current.chart_Drainage[region_Dim-1][x] = 1 + region_Current.neighbors[2][1].chart_Drainage[0][x]; + } + } + } + region_Current.chart_Max_Water_Flow = new int[region_Dim][region_Dim]; +// while(sum_Array(region_Current.chart_Drainage,region_Dim,region_Dim)>0){ +// region_Current.chart_Drainage = simulate_Drainage(region_Current.chart_Drainage,region_Current.chart_Elevation,Region.REGION_DIMENSION,Region.REGION_DIMENSION); +// for(int x = 0; x < region_Dim; x++){ +// for(int y = 0; y < region_Dim; y++){ +// if(region_Current.chart_Drainage[x][y] > region_Current.chart_Max_Water_Flow[x][y]){ +// region_Current.chart_Max_Water_Flow[x][y] = region_Current.chart_Drainage[x][y]; +// } +// } +// } +// } + region_Current.chart_Drainage = region_Current.chart_Max_Water_Flow; + open_Set.remove(region_Current); + //TODO: When adding regions to the set, check if _their_ neighbors are higher elevation recursively (probably going to need its own function) + for (int i = 0; i < 4; i++) { + if (region_Current.neighbors[adjacency_array_x[i]][adjacency_array_y[i]] != null) { + if (region_Current.neighbors[adjacency_array_x[i]][adjacency_array_y[i]].finished_Drainage_Simulation == false) { + int next_height = region_Current.neighbors[adjacency_array_x[i]][adjacency_array_y[i]].elevation_Goal; + int index_to_insert_at = 0; + Iterator region_Iterator = open_Set.iterator(); + while (region_Iterator.hasNext()) { + Region next = region_Iterator.next(); + if (next.elevation_Goal > next_height) { + index_to_insert_at++; + } else { + break; + } + } + if(!open_Set.contains(region_Current.neighbors[adjacency_array_x[i]][adjacency_array_y[i]])){ + open_Set.add(index_to_insert_at, region_Current.neighbors[adjacency_array_x[i]][adjacency_array_y[i]]); + } + } + } + } + region_Current.finished_Drainage_Simulation = true; + if(open_Set.size() > 0){ + region_Current = open_Set.get(0); + } + } + } + } + + static void create_Frame(){ + frame.setBounds(25, 25, 300 + DIMENSION, 300 + DIMENSION); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.add(graphics); + frame.setVisible(true); + frame.addKeyListener(new KeyListener(){ + public void keyTyped(KeyEvent ke) { + } + + @Override + public void keyPressed(KeyEvent ke) { + if(ke.getKeyCode() == KeyEvent.VK_C){ + increment_Current_Continent(); + } else if(ke.getKeyCode() == KeyEvent.VK_V){ + increment_Display_Toggle(); + } + } + + @Override + public void keyReleased(KeyEvent ke) { + } + } + ); + } + + static void fill_Continents(){ + for(int i = 1; i < num_Continents + 1; i++){ + Continent current = new Continent(); + continents.add(current); + //find dimensions + int min_x = -1; + int min_y = -1; + int max_x = -1; + int max_y = -1; + for(int x = 0; x < DIMENSION; x++){ + for(int y = 0; y < DIMENSION; y++){ + if(continent_Number[x][y] == i){ + if(min_x == -1){ + min_x = x; + min_y = y; + max_x = x; + max_y = y; + } else { + if (x < min_x) { + min_x = x; + } + if (x > max_x) { + max_x = x; + } + if (y < min_y) { + min_y = y; + } + if (y > max_y) { + max_y = y; + } + } + } + } + } + int dim_x = max_x - min_x + 1; + int dim_y = max_y - min_y + 1; + current.dim_x = dim_x; + current.dim_y = dim_y; + current.chart_Climate = new int[dim_x][dim_y]; + current.chart_Precipitation = new int[dim_x][dim_y]; + current.chart_Temperature = new int[dim_x][dim_y]; + current.chart_Wind_Macro = new int[dim_x][dim_y]; + current.elevation = new int[dim_x][dim_y]; + //zero out arrays + for(int x = 0; x < dim_x; x++){ + for(int y = 0; y < dim_y; y++){ + current.elevation[x][y] = 0; + current.chart_Climate[x][y] = 0; + current.chart_Precipitation[x][y] = 0; + current.chart_Temperature[x][y] = 50; + current.chart_Wind_Macro[x][y] = 0; + } + } + //fill continent level arrays + for(int x = 0; x < DIMENSION; x++){ + for(int y = 0; y < DIMENSION; y++){ + if(continent_Number[x][y] == i){ + current.size++; + current.elevation[x-min_x][y-min_y] = elevation[x][y]; + current.chart_Climate[x-min_x][y-min_y] = climate_category[x][y]; + current.chart_Precipitation[x-min_x][y-min_y] = precipitation_Chart[x][y]; + current.chart_Temperature[x-min_x][y-min_y] = temperature_Chart[x][y]; + current.chart_Wind_Macro[x-min_x][y-min_y] = wind_field[x][y].x; + } + } + } + //create regions + current.regions = new Region[dim_x][dim_y]; + for(int x = 0; x < dim_x; x++){ + for(int y = 0; y < dim_y; y++){ + if(continent_Number[x+min_x][y+min_y] == i){ + current.regions[x][y] = new Region(); + current.regions[x][y].elevation_Goal = current.elevation[x][y]; + } else { + current.regions[x][y] = null; + } + } + } + generate_Region_Elevation_Maps(current); + } + } + + static void floodfill_Continent_Number(int x, int y, int number){ + continent_Number[x][y] = number; + int offset_X[] = {0,-1,0,1}; + int offset_Y[] = {1,0,-1,0}; + for(int i = 0; i < 4; i++){ + if(x + offset_X[i] >= 0 && x + offset_Y[i] < DIMENSION){ + if(y + offset_Y[i] >= 0 && y + offset_Y[i] < DIMENSION){ + if(ocean_parsed[x + offset_X[i]][y + offset_Y[i]] < 25){ + if(continent_Number[x + offset_X[i]][y + offset_Y[i]] == 0){ + floodfill_Continent_Number(x + offset_X[i], y + offset_Y[i], number); + } + } + } + } + } + } + + static void determine_Continents(){ + for(int x = 0; x < DIMENSION; x++){ + for(int y = 0; y < DIMENSION; y++){ + if(ocean_parsed[x][y] < 25){ + if(continent_Number[x][y] == 0){ + num_Continents++; + floodfill_Continent_Number(x,y,num_Continents); + } + } + } + } + } + + /* + 0 - ocean + 1 - tropical + 2 - subtropical + 3 - temperate + 4 - dry + 5 - polar + 6 - mountainous + */ + static int[][] infer_Climate_Category(){ + int rVal[][] = new int[DIMENSION][DIMENSION]; + for(int x = 0; x < DIMENSION; x++){ + for(int y = 0; y < DIMENSION; y++) { + if (elevation[x][y] > ocean_Threshold) { + if (elevation[x][y] > mountain_Threshold) { + rVal[x][y] = 6; + } else { + if (precipitation_Chart[x][y] > 0) { + if (temperature_Chart[x][y] > 94) { + rVal[x][y] = 1; + } else if (temperature_Chart[x][y] > 80) { + rVal[x][y] = 2; + } else if (temperature_Chart[x][y] > 40) { + rVal[x][y] = 3; + } else { + rVal[x][y] = 5; + } + } else { + if (temperature_Chart[x][y] > 75) { + rVal[x][y] = 4; + } else if (temperature_Chart[x][y] > 40) { + rVal[x][y] = 4; + } else if (temperature_Chart[x][y] > 25) { + rVal[x][y] = 5; + } else { + rVal[x][y] = 5; + } + } + } + } else { + if (temperature_Chart[x][y] > 75) { + rVal[x][y] = 0; + } else if (temperature_Chart[x][y] > 40) { + rVal[x][y] = 0; + } else if (temperature_Chart[x][y] > 25) { + rVal[x][y] = 0; + } else { + rVal[x][y] = 5; + } + } + } + } + return rVal; + } + + static int[][] generate_Temperature_Chart(){ + int rVal[][] = new int[DIMENSION][DIMENSION]; + int temp = 0; + for(int x = 0; x < DIMENSION; x++){ + temp = (int)(100 - (Math.abs(x - DIMENSION/2.0) / (DIMENSION/2.0) * 100.0)); + temp = (int)(Math.sin(temp/100.0 * Math.PI / 2.0) * 100.0); + for(int y = 0; y < DIMENSION; y++){ + rVal[y][x] = temp; + } + } + return rVal; + } + + static int[][] calculate_Rain_Shadows(int[][] elevation_raws, int[][] ocean_parsed, Vector[][] wind_field){ + int rVal[][] = new int[DIMENSION][DIMENSION]; + int rain_Particles[][] = new int[DIMENSION][DIMENSION]; + int num_rain_particles = 0; + for(int x = 0; x < DIMENSION; x++){ + for(int y = 0; y < DIMENSION; y++){ + if(ocean_parsed[x][y] > 1){ + rain_Particles[x][y] = 1; + rVal[x][y] = 1; + num_rain_particles++; + } + } + } + while(num_rain_particles > DIMENSION * 2) { + for (int x = 0; x < DIMENSION; x++) { + for (int y = 0; y < DIMENSION; y++) { + if (rain_Particles[x][y] >= 1) { + if (wind_field[x][y].x == -1) { + if (x > 0) { + if (elevation_raws[x - 1][y] < rain_Shadow_Blocker_Height) { + rain_Particles[x - 1][y] = 1; + rVal[x - 1][y] = 1; + } else { + } + if (wind_field[x][y].y == -1) { + if (y > 0) { + if (elevation_raws[x - 1][y - 1] < rain_Shadow_Blocker_Height) { + rain_Particles[x - 1][y - 1] = 1; + rVal[x - 1][y - 1] = 1; + } + } + } else if (wind_field[x][y].y == 1) { + if (y < DIMENSION - 1) { + if (elevation_raws[x - 1][y + 1] < rain_Shadow_Blocker_Height) { + rain_Particles[x - 1][y + 1] = 1; + rVal[x - 1][y + 1] = 1; + } + } + } + } else { + } + } else { + if (x < DIMENSION - 1) { + if (elevation_raws[x + 1][y] < rain_Shadow_Blocker_Height) { + rain_Particles[x + 1][y] = 1; + rVal[x + 1][y] = 1; + } else { + } + if (wind_field[x][y].y == -1) { + if (y > 0) { + if (elevation_raws[x + 1][y - 1] < rain_Shadow_Blocker_Height) { + rain_Particles[x + 1][y - 1] = 1; + rVal[x + 1][y - 1] = 1; + } + } + } else if (wind_field[x][y].y == 1) { + if (y < DIMENSION - 1) { + if (elevation_raws[x + 1][y + 1] < rain_Shadow_Blocker_Height) { + rain_Particles[x + 1][y + 1] = 1; + rVal[x + 1][y + 1] = 1; + } + } + } + } else { + } + } + rain_Particles[x][y] = 0; + } + } + } + num_rain_particles = 0; + for(int x = 0; x < DIMENSION; x++){ + for(int y = 0; y < DIMENSION; y++){ + if(rain_Particles[x][y] == 1){ + num_rain_particles++; + } + } + } + } + return rVal; + } + + static Vector[][] map_Wind_Field(){ + Vector rVal[][] = new Vector[DIMENSION][DIMENSION]; + int sixth = (int)(DIMENSION / 6.0); + for(int x = 0; x < DIMENSION; x++){ + for(int y = 0; y < DIMENSION; y++){ + if(x < sixth){ + rVal[x][y] = new Vector(); + rVal[x][y].x = -1; + rVal[x][y].y = -1; + } else if(x < sixth * 2){ + rVal[x][y] = new Vector(); + rVal[x][y].x = 1; + rVal[x][y].y = 1; + } else if(x < sixth * 3){ + rVal[x][y] = new Vector(); + rVal[x][y].x = -1; + rVal[x][y].y = -1; + } else if(x < sixth * 4){ + rVal[x][y] = new Vector(); + rVal[x][y].x = -1; + rVal[x][y].y = 1; + } else if(x < sixth * 5){ + rVal[x][y] = new Vector(); + rVal[x][y].x = 1; + rVal[x][y].y = -1; + } else { + rVal[x][y] = new Vector(); + rVal[x][y].x = -1; + rVal[x][y].y = 1; + } + } + } + return rVal; + } + + static int[][] parse_Oceans(int[][] data){ + int rVal[][] = new int[DIMENSION][DIMENSION]; + for(int x = 0; x < DIMENSION; x++){ + for(int y = 0; y < DIMENSION; y++){ + if(data[x][y] < ocean_Threshold){ + rVal[x][y] = ocean_Threshold; + } else { + rVal[x][y] = 0; + } + } + } + return rVal; + } + + static int[][] parse_Mountainscapes(int[][] data){ + int rVal[][] = new int[DIMENSION][DIMENSION]; + for(int x = 0; x < DIMENSION; x++){ + for(int y = 0; y < DIMENSION; y++){ + if(data[x][y] > mountain_Threshold){ + rVal[x][y] = mountain_Threshold; + } else { + rVal[x][y] = 0; + } + } + } + return rVal; + } + + static int[][] closed_Set; + + static int floodfill_Reach_Threshold(int[][] data, int[][] closed_Set, int x, int y, int min_val, int max_val){ + int rVal = 0; + int num_hits; + int offset_X[] = {-1,-1,-1,0,0,0,1,1,1}; + int offset_Y[] = {-1,0,1,-1,0,1,-1,0,1}; + if(data[x][y] > min_val && data[x][y] < max_val){ + closed_Set[x][y] = 1; + for(int i = 0; i < 9; i++){ + if(x + offset_X[i] >= 0 && + x + offset_X[i] < DIMENSION && + y + offset_Y[i] >= 0 && + y + offset_Y[i] < DIMENSION){ + if(closed_Set[x + offset_X[i]][y+offset_Y[i]] == 0){ + rVal = rVal + floodfill_Reach_Threshold(data,closed_Set,x + offset_X[i],y + offset_Y[i], min_val, max_val); + } + } + } + } + return rVal; + } + + static int[][] remove_Small_Mountain_Ranged(int[][] data){ + int rVal[][] = new int[DIMENSION][DIMENSION]; + boolean removed_Something = false; +// for (int x = 0; x < DIMENSION; x++) { +// for (int y = 0; y < DIMENSION; y++) { +// rVal[x][y] = data[x][y]; +// } +// } +// while (true) { + for (int x = 0; x < DIMENSION; x++) { + for (int y = 0; y < DIMENSION; y++) { + rVal[x][y] = data[x][y]; + if (data[x][y] > mountain_Threshold) { + rVal[x][y] = rVal[x][y] - 5; +// closed_Set = new int[DIMENSION][DIMENSION]; +// if (floodfill_Reach_Threshold(rVal, closed_Set, x, y, mountain_Threshold, 101) < mountain_Range_Size_Minimum) { +// rVal[x][y] = rVal[x][y] - 25; +//// removed_Something = true; +// } + } + } + } +// if(!removed_Something){ +// break; +//// } else { +// removed_Something = false; +// } +// } + return rVal; + } + + static int[][] compression_filter(int[][] data){ + int rVal[][] = new int[DIMENSION][DIMENSION]; + for(int x = 0; x < DIMENSION; x++){ + for(int y = 0; y < DIMENSION; y++){ + if(data[x][y] < 25){ + rVal[x][y] = (int)(data[x][y] * 3.0 / 2.0); + } else if(data[x][y] > 75){ + rVal[x][y] = (int)(data[x][y] / 3.0 * 2.0); + } else { + rVal[x][y] = data[x][y]; + } + } + } + return rVal; + } + + static int[][] three_halves_filter(int[][] data){ + int rVal[][] = new int[DIMENSION][DIMENSION]; + for(int x = 0; x < DIMENSION; x++){ + for(int y = 0; y < DIMENSION; y++){ + rVal[x][y] = (int)(data[x][y] * 3.0 / 2.0); + if(rVal[x][y] > 100){ + rVal[x][y] = 100; + } + } + } + return rVal; + } + + static int[][] two_third_filter(int[][] data){ + int rVal[][] = new int[DIMENSION][DIMENSION]; + for(int x = 0; x < DIMENSION; x++){ + for(int y = 0; y < DIMENSION; y++){ + rVal[x][y] = (int)(data[x][y] * 2.0 / 3.0); + if(rVal[x][y] < 0){ + rVal[x][y] = 0; + } + } + } + return rVal; + } + + static int[][] high_end_two_third_filter(int[][] data){ + int rVal[][] = new int[DIMENSION][DIMENSION]; + for(int x = 0; x < DIMENSION; x++){ + for(int y = 0; y < DIMENSION; y++) { + if (data[x][y] > 50) { + rVal[x][y] = (int) (data[x][y] * 2.0 / 3.0); + if (rVal[x][y] < 0) { + rVal[x][y] = 0; + } + } else { + rVal[x][y] = data[x][y]; + } + } + } + return rVal; + } + + static int[][] reduce_mid_high_end_filter(int[][] data){ + int rVal[][] = new int[DIMENSION][DIMENSION]; + for(int x = 0; x < DIMENSION; x++){ + for(int y = 0; y < DIMENSION; y++) { + if (data[x][y] > 50 && data[x][y] < 75) { + rVal[x][y] = (int) (data[x][y] * 2.0 / 3.0); + if (rVal[x][y] < 0) { + rVal[x][y] = 0; + } + } else { + rVal[x][y] = data[x][y]; + } + } + } + return rVal; + } + + static int[][] small_Kernel_Smooth(int elevation_Map[][]){ + int rVal[][] = new int[DIMENSION][DIMENSION]; + int kernel_offset_x[] = { + -1,0,1, + -1,0,1, + -1,0,1 + }; + int kernel_offset_y[] = { + -1,-1,-1, + 0,0,0, + 1,1,1 + }; + int kernle_modifier[] = { + 1,2,1, + 2,4,2, + 1,2,1 + }; + for(int x = 1; x < DIMENSION - 1; x++){ + for(int y = 1; y < DIMENSION - 1; y++){ + int sum = 0; + for(int i = 0; i < 9; i++){ + sum = sum + elevation_Map[x+kernel_offset_x[i]][y+kernel_offset_y[i]] * kernle_modifier[i]; + } + sum = (int)(sum/13.0); + if(sum > 100){ + sum = 100; + } + rVal[x][y] = sum; + } + } + return rVal; + } + + static int[][] small_Kernel_Sharpen(int elevation_Map[][]){ + int rVal[][] = new int[DIMENSION][DIMENSION]; + int kernel_offset_x[] = { + -1,0,1, + -1,0,1, + -1,0,1 + }; + int kernel_offset_y[] = { + -1,-1,-1, + 0,0,0, + 1,1,1 + }; + int kernel_modifier[] = { + 0,-1,0, + -1,5,-1, + 0,-1,0 + }; + for(int x = 1; x < DIMENSION - 1; x++){ + for(int y = 1; y < DIMENSION - 1; y++){ + int sum = 0; + for(int i = 0; i < 9; i++){ + sum = sum + elevation_Map[x+kernel_offset_x[i]][y+kernel_offset_y[i]] * kernel_modifier[i]; + } +// sum = (int)(sum/13.0); + if(sum > 100){ + sum = 100; + } + if(sum < 0){ + sum = 0; + } + rVal[x][y] = sum; + } + } + return rVal; + } + + static int[][] smooth_Terrain_Further(int elevation_Map[][]){ + int rVal[][] = new int[DIMENSION][DIMENSION]; + int kernel_offset_x[] = {-2,-1,0,1,2, + -2,-1,0,1,2, + -2,-1,0,1,2, + -2,-1,0,1,2, + -2,-1,0,1,2, + }; + int kernel_offset_y[] = {-2,-2,-2,-2,-2, + -1,-1,-1,-1,-1, + 0,0,0,0,0, + 1,1,1,1,1, + 2,2,2,2,2}; + int kernel_modifier[] = { + 1,4,6,4,1, + 4,16,24,16,4, + 6,24,36,24,6, + 4,16,24,16,4, + 1,4,6,4,1 + }; + for(int x = 2; x < DIMENSION - 2; x++){ + for(int y = 2; y < DIMENSION - 2; y++){ + int sum = 0; + for(int i = 0; i < 25; i++){ + sum = sum + elevation_Map[x+kernel_offset_x[i]][y+kernel_offset_y[i]] * kernel_modifier[i]; + } + sum = (int)(sum/256.0); + if(sum > 100){ + sum = 100; + } + rVal[x][y] = sum; + } + } + return rVal; + } + + static int[][] land_exponential_filter(int[][] data){ + int rVal[][] = new int[DIMENSION][DIMENSION]; + for(int x = 0; x < DIMENSION; x++){ + for(int y = 0; y < DIMENSION; y++){ + if(data[x][y] > TerrainGen.ocean_Threshold && data[x][y] < 90){ + +// System.out.print((data[x][y] - ocean_Threshold) + "/" + (100.0 - ocean_Threshold) + "=" + ((data[x][y] - ocean_Threshold) / (100.0 - ocean_Threshold)) + " "); +// System.out.print(data[x][y] + "->"); + rVal[x][y] = (int)(data[x][y] * Math.exp(-(1.0 - ((data[x][y] - ocean_Threshold) / (100.0 - ocean_Threshold)))) * 0.2f + data[x][y] * 0.8f); + if(rVal[x][y] < TerrainGen.ocean_Threshold){ + rVal[x][y] = TerrainGen.ocean_Threshold + 1; + } +// System.out.println(rVal[x][y]); + if(Math.exp(-(1.0 - ((data[x][y] - ocean_Threshold) / (100.0 - ocean_Threshold)))) > 1.0){ + System.out.println("WARNING!!"); + } + } else { + rVal[x][y] = data[x][y]; + } + } + } + return rVal; + } + + int[][] generate_Interpolation(int[][] data){ + /* + Looks at 3x3 kernel + */ + int kernel_offset_x[] = {-1,0,1,-1,0,1,-1,0,1}; + int kernel_offset_y[] = {1,1,1,0,0,0,-1,-1,-1}; + int rVal[][] = new int[100][100]; + return rVal; + } + + static int[][] interpolate_Elevation_Raws(int[][] raw){ + int rVal[][] = new int[DIMENSION * 2][DIMENSION * 2]; + for(int x = 0; x < DIMENSION - 1; x++){ + for(int y = 0; y < DIMENSION - 1; y++){ + rVal[x*2][y*2] = raw[x][y]; + rVal[x*2+1][y*2] = (raw[x][y] + raw[x+1][y])/2; + rVal[x*2][y*2+1] = (raw[x][y] + raw[x][y+1])/2; + rVal[x*2+1][y*2+1] = (raw[x][y] + raw[x+1][y+1])/2; + } + } + return rVal; + } + + static int[][] get_Pregened_Data(){ + int rVal[][] = {{0, 0, 0, 0, 0, 0, 0, 0, 2, 10, 11, 24, 19, 99, 99, 70, 39, 11, 10, 0, 0, 2, 8, 0, 0, 0, 0, 0, 28, 2, 9, 17, 30, 51, 58, 4, 57, 36, 13, 22, 13, 14, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 1, 12, 1, 15, 4, 57, 39, 6, 15, 23, 22, 5, 17, 3, 0, 10, 0, 10, 0, 0, 1, 3, 1, 9, 5, 12, 6, 13, 6, 38, 33, 78, 30, 36, 55, 1, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 19, 23, 47, 1, 27, 30, 12, 9, 2, 0, 5, 12, 0, 10, 0, 0, 3, 0, 3, 11, 32, 16, 1, 4, 21, 5, 3, 29, 6, 3, 11, 8, 26, 11, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 32, 34, 13, 26, 0, 99, 1, 29, 68, 13, 0, 3, 0, 0, 0, 0, 5, 23, 3, 4, 38, 13, 26, 0, 20, 3, 25, 16, 42, 6, 55, 19, 8, 3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 36, 21, 0, 48, 7, 40, 22, 25, 1, 16, 2, 0, 13, 5, 0, 0, 2, 6, 27, 20, 12, 19, 32, 30, 27, 29, 14, 1, 31, 46, 12, 50, 32, 21, 13, 16, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 31, 28, 19, 14, 17, 20, 7, 1, 1, 64, 4, 15, 4, 10, 32, 0, 0, 0, 1, 1, 3, 0, 16, 20, 9, 23, 7, 9, 20, 4, 0, 2, 6, 2, 18, 14, 13, 42, 0, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 15, 5, 26, 12, 27, 7, 19, 28, 4, 36, 39, 30, 28, 12, 12, 5, 0, 9, 18, 4, 6, 1, 29, 11, 12, 4, 1, 4, 22, 3, 10, 1, 0, 2, 1, 34, 1, 0, 10, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 2, 13, 16, 3, 19, 27, 0, 25, 69, 38, 21, 33, 44, 3, 1, 7, 11, 0, 6, 2, 20, 23, 6, 3, 8, 21, 15, 27, 5, 0, 5, 1, 45, 1, 19, 16, 40, 0, 0, 14, 11, 12, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 5, 23, 21, 35, 9, 39, 58, 28, 7, 21, 30, 33, 3, 7, 16, 11, 9, 14, 5, 19, 14, 16, 21, 41, 5, 14, 22, 13, 17, 20, 17, 6, 25, 18, 14, 23, 10, 15, 20, 3, 7, 4, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 7, 60, 13, 5, 35, 33, 7, 21, 35, 25, 28, 24, 3, 38, 17, 30, 3, 4, 21, 4, 21, 37, 24, 2, 0, 13, 22, 0, 3, 15, 2, 24, 0, 13, 27, 8, 34, 3, 11, 0, 18, 22, 24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 9, 0, 3, 5, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 4, 5, 1, 49, 26, 28, 11, 5, 4, 15, 17, 5, 34, 25, 40, 13, 35, 10, 81, 20, 15, 21, 21, 18, 19, 10, 0, 17, 5, 1, 10, 10, 20, 11, 15, 10, 30, 23, 45, 24, 30, 35, 21, 36, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 14, 27, 17, 10, 26, 12, 19, 2, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 1, 0, 46, 12, 17, 0, 1, 24, 11, 10, 9, 3, 25, 45, 36, 45, 1, 52, 14, 0, 24, 24, 33, 37, 7, 22, 3, 2, 6, 18, 11, 0, 16, 5, 7, 19, 5, 0, 15, 39, 31, 4, 24, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 10, 41, 99, 1, 36, 32, 1, 11, 11, 33, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 2, 11, 6, 1, 0, 19, 14, 0, 0, 0, 34, 26, 63, 20, 46, 34, 9, 24, 37, 53, 25, 20, 2, 14, 2, 2, 0, 5, 8, 1, 21, 11, 3, 28, 2, 4, 36, 8, 34, 31, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 23, 43, 23, 24, 31, 62, 24, 2, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 25, 0, 0, 14, 20, 16, 0, 0, 0, 0, 35, 58, 60, 77, 29, 24, 44, 11, 0, 5, 1, 4, 0, 4, 3, 0, 0, 0, 2, 34, 12, 0, 52, 15, 22, 37, 2, 14, 26, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 32, 31, 10, 42, 32, 21, 29, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 2, 0, 0, 0, 0, 27, 47, 82, 33, 22, 28, 20, 22, 21, 7, 6, 1, 0, 0, 0, 2, 0, 0, 0, 10, 15, 9, 27, 0, 1, 0, 5, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 74, 19, 31, 0, 9, 4, 27, 22, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 58, 13, 13, 46, 47, 4, 47, 14, 35, 3, 5, 4, 18, 4, 0, 0, 0, 0, 1, 1, 10, 0, 4, 0, 0, 0, 8, 0, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 89, 20, 13, 5, 4, 3, 5, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 38, 30, 39, 18, 24, 8, 6, 4, 0, 0, 1, 1, 4, 1, 2, 1, 0, 0, 6, 6, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 31, 2, 45, 30, 72, 68, 9, 27, 2, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 6, 8, 2, 6, 15, 7, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 99, 21, 23, 0, 2, 33, 1, 22, 44, 8, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 29, 4, 1, 1, 1, 4, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 13, 9, 31, 7, 29, 30, 23, 2, 36, 6, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 13, 4, 5, 2, 5, 2, 8, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 47, 39, 11, 16, 41, 40, 56, 16, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 12, 13, 11, 13, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 45, 16, 24, 14, 3, 27, 84, 23, 23, 11, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 5, 1, 4, 0, 1, 8, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 18, 9, 1, 22, 31, 57, 9, 46, 33, 19, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 4, 2, 1, 10, 8, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 9, 11, 14, 45, 46, 34, 31, 27, 43, 16, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 3, 0, 3, 1, 2, 7, 0, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 2, 30, 23, 30, 7, 30, 14, 20, 49, 79, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 35, 10, 14, 33, 54, 30, 61, 38, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 22, 24, 46, 7, 30, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 11, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 5, 0, 0, 0, 5, 17, 20, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 2, 2, 3, 0, 13, 23, 11, 10, 14, 0, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 11, 1, 0, 27, 11, 10, 5, 21, 7, 14, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 41, 29, 9, 1, 8, 2, 12, 72, 16, 0, 7, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 6, 17, 4, 10, 25, 13, 15, 16, 12, 1, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 22, 10, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 21, 0, 0, 0, 1, 3, 45, 31, 1, 91, 73, 2, 50, 0, 19, 2, 3, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 2, 17, 15, 87, 9, 30, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 9, 17, 0, 0, 0, 0, 0, 36, 6, 19, 46, 8, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 34, 38, 48, 15, 39, 1, 2, 13, 5, 9, 35, 24, 36, 26, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 25, 23, 0, 0, 0, 0, 0, 0, 37, 27, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 28, 27, 54, 21, 3, 8, 36, 2, 0, 8, 18, 37, 11, 75, 20, 9, 5, 2, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 29, 27, 1, 0, 0, 0, 0, 0, 99, 92, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 17, 81, 40, 45, 15, 42, 33, 5, 37, 55, 28, 14, 8, 8, 4, 20, 9, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 99, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 14, 12, 43, 71, 19, 27, 59, 23, 61, 31, 23, 28, 48, 12, 27, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 99, 97, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 16, 29, 12, 7, 57, 46, 21, 4, 15, 56, 6, 46, 54, 0, 17, 9, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 20, 40, 42, 35, 5, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 24, 8, 1, 48, 43, 20, 26, 8, 11, 7, 13, 25, 26, 23, 5, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 6, 25, 50, 99, 19, 0, 15, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 6, 11, 0, 3, 68, 3, 1, 32, 27, 23, 20, 6, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 1, 27, 99, 87, 43, 50, 5, 1, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 2, 24, 45, 45, 15, 23, 71, 16, 24, 2, 25, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 8, 11, 0, 1, 20, 99, 6, 20, 27, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 3, 41, 36, 14, 16, 30, 46, 59, 58, 32, 29, 32, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 11, 12, 0, 17, 11, 99, 99, 59, 22, 19, 21, 3, 3, 11, 0, 7, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 3, 99, 14, 6, 7, 37, 30, 8, 0, 37, 18, 2, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 9, 2, 2, 10, 11, 9, 31, 95, 99, 43, 5, 10, 12, 9, 7, 16, 24, 6, 1, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 8, 18, 26, 2, 19, 23, 28, 20, 78, 8, 1, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 12, 14, 9, 15, 12, 2, 28, 59, 41, 28, 4, 2, 8, 19, 1, 3, 4, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 8, 9, 2, 28, 20, 11, 31, 4, 26, 19, 18, 15, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 5, 22, 12, 23, 7, 3, 6, 19, 78, 22, 10, 3, 5, 13, 21, 2, 10, 15, 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 15, 1, 2, 9, 39, 14, 28, 27, 2, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 5, 1, 9, 30, 15, 0, 4, 3, 17, 27, 2, 25, 40, 7, 13, 25, 6, 15, 8, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 5, 23, 0, 1, 47, 17, 8, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 15, 5, 4, 44, 10, 19, 2, 1, 0, 3, 9, 5, 9, 8, 13, 3, 21, 7, 19, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 3, 13, 23, 5, 7, 4, 17, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 21, 2, 0, 24, 2, 4, 4, 2, 16, 0, 1, 21, 10, 2, 2, 2, 0, 26, 11, 3, 6, 2, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 27, 12, 0, 33, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 5, 2, 18, 13, 11, 18, 4, 3, 2, 0, 3, 11, 18, 9, 5, 10, 8, 5, 0, 0, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 5, 19, 8, 7, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 3, 4, 0, 1, 5, 1, 5, 2, 0, 2, 2, 1, 3, 2, 4, 2, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 3, 18, 1, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 8, 1, 0, 0, 2, 5, 7, 3, 2, 1, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 1, 0, 4, 2, 0}, +{0, 0, 0, 0, 0, 0, 7, 1, 10, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 1, 4, 2}, +{0, 0, 0, 0, 0, 0, 0, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 5, 1, 3, 9, 0, 0, 0, 0, 0, 0, 0, 4, 2, 3, 1, 0, 1, 3, 0, 5}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 13, 2, 19, 10, 1, 0, 0, 0, 0, 1, 5, 7, 1, 3, 7, 1, 0, 5}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 10, 19, 14, 19, 11, 23, 16, 3, 6, 5, 0, 0, 0, 0, 1, 0, 0, 5, 1, 3, 1, 17, 2}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 2, 15, 7, 6, 3, 10, 4, 21, 0, 3, 4, 10, 0, 0, 0, 2, 1, 0, 4, 2, 4, 7, 18}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 14, 3, 0, 11, 26, 2, 10, 20, 3, 12, 11, 30, 0, 0, 0, 0, 1, 1, 2, 0, 2, 1, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 33, 21, 7, 39, 29, 4, 13, 6, 4, 12, 7, 11, 0, 0, 0, 0, 0, 0, 1, 5, 1, 3, 0, 18, 26}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 16, 11, 11, 0, 18, 5, 4, 7, 7, 1, 14, 3, 0, 3, 1, 1, 1, 0, 0, 2, 1, 7, 4, 8, 11}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 1, 21, 27, 9, 12, 10, 4, 14, 3, 3, 0, 13, 5, 2, 3, 3, 5, 14, 3, 19, 0, 3, 2, 8, 12}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 6, 2, 0, 4, 28, 4, 9, 20, 7, 23, 6, 0, 6, 7, 4, 4, 23, 8, 5, 7, 11, 9, 8, 5}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 1, 1, 6, 10, 51, 17, 32, 1, 11, 3, 5, 10, 0, 1, 6, 12, 0, 18, 8, 0, 0, 1, 14}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 5, 9, 16, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 20, 1, 7, 0, 22, 4, 8, 3, 23, 5, 27, 6, 1, 24, 2, 2, 11, 10, 10, 1, 0, 5}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 1, 1, 20, 9, 3, 3, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 6, 8, 1, 8, 17, 0, 9, 1, 11, 2, 14, 10, 6, 25, 0, 10, 5, 14, 2, 8, 0, 1, 6, 5, 1}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 21, 7, 3, 0, 15, 11, 2, 25, 0, 1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 5, 10, 26, 31, 17, 8, 11, 18, 10, 5, 0, 2, 1, 14, 8, 32, 8, 12, 10, 12, 5, 0, 9, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 20, 2, 0, 0, 7, 13, 2, 0, 0, 15, 28, 9, 4, 19, 35, 10, 0, 12, 6, 5, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 6, 12, 0, 11, 0, 4, 15, 18, 24, 10, 11, 35, 3, 1, 4, 17, 13, 2, 6, 0, 11, 2, 2}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 6, 0, 10, 7, 25, 11, 5, 6, 0, 0, 10, 8, 7, 41, 17, 14, 1, 10, 20, 11, 13, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 19, 2, 1, 12, 21, 4, 7, 4, 3, 1, 19, 0, 0, 14, 9, 21, 0, 1, 15, 0, 1, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 9, 4, 9, 22, 10, 0, 46, 12, 3, 3, 2, 1, 0, 11, 5, 65, 7, 13, 14, 5, 5, 21, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 10, 0, 1, 6, 18, 1, 11, 3, 6, 24, 2, 1, 0, 24, 2, 4, 1, 1, 0, 3, 1}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 2, 7, 4, 18, 2, 0, 7, 11, 2, 5, 15, 20, 1, 6, 11, 6, 13, 3, 2, 17, 15, 6, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 16, 7, 6, 22, 0, 7, 12, 16, 2, 10, 1, 7, 17, 3, 3, 12, 19, 2, 2, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 25, 15, 21, 4, 2, 0, 18, 1, 0, 2, 9, 9, 0, 3, 4, 27, 16, 38, 1, 11, 13, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 1, 0, 9, 7, 12, 23, 0, 11, 32, 3, 20, 5, 2, 5, 1, 0, 5, 5, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 8, 33, 11, 4, 4, 3, 1, 5, 5, 0, 0, 0, 2, 5, 5, 2, 10, 9, 15, 16, 28, 11, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 7, 1, 2, 0, 2, 9, 3, 2, 32, 30, 0, 3, 2, 3, 5, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 20, 23, 8, 22, 2, 3, 9, 3, 14, 6, 1, 0, 0, 0, 0, 0, 1, 2, 0, 48, 1, 26, 44, 1, 2, 8, 14, 6, 4, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 1, 10, 0, 0, 0, 0, 0, 1, 11, 3, 15, 7, 15, 11, 2, 9, 4, 4, 0, 7}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 6, 11, 6, 22, 9, 7, 5, 20, 18, 7, 1, 1, 1, 0, 0, 0, 2, 4, 10, 24, 1, 14, 14, 33, 8, 18, 4, 23, 32, 3, 2, 11, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 28, 7, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 1, 5, 12, 0, 9, 51, 0, 12, 0, 0, 1, 4, 6, 13, 4, 1, 4, 8, 5, 6, 14, 18, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 1, 23, 2, 26, 8, 8, 0, 24, 12, 48, 2, 39, 1, 20, 0, 1, 0, 0, 7, 28, 14, 14, 2, 8, 40, 20, 9, 16, 25, 9, 9, 6, 6, 1, 11, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 4, 8, 4, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 8, 1, 6, 5, 0, 0, 5, 0, 0, 0, 4, 6, 26, 11, 16, 1, 22, 1, 14, 25, 0, 6, 3}, +{0, 0, 0, 0, 0, 0, 2, 5, 11, 11, 10, 2, 21, 2, 4, 20, 18, 2, 22, 0, 23, 26, 1, 17, 2, 1, 22, 0, 8, 2, 13, 56, 14, 22, 47, 99, 39, 4, 11, 5, 3, 14, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 11, 11, 2, 9, 16, 9, 4, 0, 0, 0, 0, 0, 9, 2, 0, 7, 6, 14, 9, 39, 13, 2, 0, 7, 1, 15, 3, 5, 46, 9, 13, 4, 0, 0, 10, 1}, +{0, 0, 0, 0, 0, 0, 0, 14, 13, 8, 12, 0, 5, 1, 4, 23, 14, 33, 0, 67, 9, 15, 5, 7, 6, 1, 8, 17, 37, 25, 6, 23, 16, 33, 37, 15, 4, 36, 30, 9, 20, 4, 25, 9, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 10, 1, 0, 23, 12, 6, 22, 15, 9, 4, 6, 0, 2, 0, 0, 0, 4, 0, 0, 10, 8, 6, 25, 22, 9, 36, 0, 1, 5, 5, 18, 11, 15, 19, 0, 32, 7, 0, 3, 0}, +{0, 0, 0, 0, 2, 15, 2, 3, 5, 11, 27, 0, 23, 8, 28, 9, 8, 3, 8, 16, 13, 28, 20, 7, 20, 1, 23, 42, 16, 6, 25, 41, 29, 43, 58, 2, 26, 27, 13, 21, 22, 22, 46, 7, 15, 6, 18, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 6, 11, 12, 3, 19, 19, 28, 5, 4, 19, 0, 0, 0, 0, 0, 6, 8, 8, 1, 10, 3, 4, 12, 1, 7, 14, 9, 0, 15, 4, 1, 17, 5, 2, 11, 8, 8, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 5, 0, 41, 11, 27, 12, 1, 13, 13, 10, 16, 4, 2, 17, 6, 4, 1, 1, 2, 25, 24, 42, 10, 15, 0, 26, 26, 29, 18, 99, 1, 40, 38, 19, 2, 24, 8, 4, 25, 41, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 12, 0, 99, 13, 6, 5, 27, 3, 16, 4, 23, 5, 0, 0, 0, 6, 7, 8, 2, 13, 1, 28, 7, 5, 19, 14, 14, 0, 1, 27, 15, 3, 3, 2, 9, 7, 5, 1}, +{0, 0, 0, 0, 0, 0, 0, 0, 4, 21, 13, 7, 0, 6, 22, 4, 3, 10, 6, 40, 0, 6, 10, 40, 27, 17, 2, 1, 28, 0, 5, 28, 7, 10, 39, 6, 34, 52, 35, 8, 78, 6, 10, 41, 25, 33, 3, 36, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 4, 8, 32, 11, 15, 35, 30, 37, 19, 28, 30, 35, 14, 0, 0, 0, 10, 18, 4, 4, 54, 7, 21, 6, 6, 17, 5, 7, 0, 0, 1, 8, 7, 19, 8, 9, 0, 0, 0, 3}, +{0, 0, 0, 0, 0, 0, 3, 0, 21, 10, 4, 15, 22, 1, 1, 5, 8, 2, 29, 23, 21, 2, 30, 2, 1, 3, 13, 57, 5, 26, 5, 32, 44, 8, 22, 36, 46, 0, 10, 21, 10, 7, 10, 25, 3, 2, 14, 17, 7, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 14, 12, 20, 8, 5, 1, 27, 0, 1, 19, 22, 25, 18, 2, 1, 16, 0, 2, 1, 0, 28, 2, 1, 1, 6, 15, 11, 0, 0, 0, 0, 12, 4, 10, 8, 3, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 10, 4, 22, 9, 2, 2, 6, 4, 8, 7, 7, 16, 16, 18, 11, 27, 32, 34, 49, 4, 99, 14, 4, 63, 34, 5, 32, 9, 3, 18, 48, 51, 12, 8, 41, 10, 7, 8, 30, 12, 1, 26, 26, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 12, 7, 11, 34, 3, 23, 18, 32, 5, 3, 36, 11, 6, 2, 0, 30, 29, 25, 0, 4, 4, 12, 3, 16, 6, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 7, 6, 19, 13, 6, 0, 1, 3, 1, 0, 3, 20, 4, 13, 0, 3, 6, 26, 1, 59, 42, 99, 21, 17, 26, 69, 5, 2, 47, 36, 88, 9, 6, 23, 47, 18, 11, 4, 25, 33, 32, 3, 41, 5, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 1, 8, 4, 12, 6, 30, 23, 1, 5, 18, 50, 19, 24, 8, 10, 8, 1, 6, 8, 1, 1, 4, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 5, 7, 0, 0, 0, 0, 0, 2, 1, 4, 8, 7, 1, 9, 40, 13, 68, 39, 18, 1, 27, 27, 23, 30, 13, 23, 12, 9, 20, 24, 68, 27, 45, 29, 34, 31, 22, 13, 4, 19, 8, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 7, 3, 6, 0, 1, 11, 5, 10, 11, 22, 10, 25, 17, 8, 7, 1, 3, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 11, 1, 0, 0, 0, 0, 0, 0, 1, 6, 1, 13, 19, 2, 67, 99, 33, 7, 6, 1, 29, 46, 5, 17, 53, 5, 0, 41, 20, 17, 9, 52, 21, 19, 47, 19, 0, 26, 9, 0, 14, 1, 1, 4, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 21, 11, 3, 8, 2, 27, 53, 36, 20, 50, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 5, 9, 9, 11, 10, 6, 3, 50, 99, 82, 21, 31, 27, 15, 8, 6, 6, 28, 3, 66, 7, 1, 22, 9, 76, 4, 13, 46, 50, 6, 20, 6, 2, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 13, 2, 15, 18, 7, 3, 8, 18, 3, 12, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 1, 0, 0, 2, 6, 2, 1, 21, 42, 99, 47, 16, 47, 12, 14, 3, 26, 3, 37, 51, 18, 1, 5, 13, 4, 40, 26, 45, 28, 1, 47, 16, 8, 2, 2, 2, 0, 0, 3, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 39, 32, 25, 9, 22, 3, 7, 14, 75, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 6, 16, 11, 20, 13, 3, 68, 99, 20, 15, 10, 15, 0, 8, 15, 3, 3, 14, 27, 25, 27, 3, 0, 13, 12, 31, 8, 2, 18, 4, 13, 0, 0, 0, 5, 0, 2, 9, 0, 0, 1, 4, 1, 0, 0, 2, 3, 7, 2, 1, 11, 17, 2, 10, 11, 1, 32, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 3, 5, 4, 3, 4, 7, 11, 9, 15, 43, 14, 17, 2, 18, 0, 12, 5, 26, 7, 11, 7, 42, 22, 19, 0, 5, 5, 5, 3, 9, 11, 0, 3, 0, 0, 2, 3, 17, 1, 3, 0, 0, 0, 11, 0, 7, 0, 0, 14, 6, 1, 36, 8, 5, 99, 2, 30, 21, 20, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 3, 5, 0, 5, 8, 3, 4, 0, 6, 0, 4, 9, 3, 5, 3, 28, 21, 35, 20, 25, 35, 26, 12, 7, 41, 32, 22, 9, 25, 6, 0, 2, 0, 0, 6, 3, 6, 12, 1, 15, 4, 2, 1, 0, 2, 0, 30, 21, 2, 1, 15, 21, 1, 34, 12, 25, 21, 11, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 9, 13, 1, 4, 11, 0, 2, 27, 3, 6, 6, 0, 0, 1, 0, 33, 31, 16, 22, 14, 20, 3, 0, 1, 54, 15, 3, 22, 2, 0, 0, 0, 0, 1, 4, 0, 3, 2, 4, 2, 0, 0, 2, 13, 5, 8, 11, 4, 4, 39, 23, 35, 27, 3, 10, 14, 21, 12, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 6, 0, 2, 0, 2, 1, 1, 19, 7, 4, 11, 10, 2, 0, 21, 0, 16, 65, 1, 20, 2, 2, 42, 30, 0, 0, 0, 2, 0, 0, 0, 0, 0, 4, 3, 9, 4, 7, 0, 5, 17, 10, 1, 11, 4, 1, 7, 18, 0, 6, 22, 1, 15, 2, 12, 0, 2, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 1, 1, 10, 13, 2, 1, 2, 0, 1, 8, 1, 6, 9, 6, 0, 10, 18, 37, 24, 12, 15, 4, 35, 4, 10, 18, 24, 20, 0, 0, 0, 0, 0, 3, 1, 4, 11, 0, 0, 0, 1, 7, 2, 1, 6, 5, 22, 34, 21, 12, 22, 24, 3, 50, 4, 15, 5, 1, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 4, 1, 0, 4, 14, 0, 3, 3, 1, 0, 1, 5, 4, 17, 1, 13, 3, 24, 30, 25, 27, 4, 10, 21, 2, 2, 0, 6, 0, 0, 0, 0, 0, 3, 1, 2, 6, 11, 3, 22, 13, 1, 19, 8, 14, 1, 3, 4, 5, 8, 12, 2, 25, 1, 7, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 4, 0, 3, 0, 3, 1, 6, 2, 2, 0, 0, 8, 0, 2, 5, 1, 0, 42, 52, 8, 27, 24, 41, 12, 22, 7, 4, 10, 0, 0, 0, 0, 0, 0, 1, 2, 0, 4, 5, 15, 7, 7, 10, 0, 17, 11, 7, 25, 2, 84, 28, 13, 0, 3, 4, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 11, 2, 7, 2, 2, 5, 28, 11, 5, 0, 2, 1, 8, 0, 0, 0, 0, 0, 0, 0, 1, 8, 3, 25, 7, 5, 35, 5, 25, 69, 22, 60, 53, 8, 11, 3, 4, 1, 1, 1, 8, 6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; + return rVal; + } + + static int[][] load_Data(String path){ + int rVal[][] = null; + try { + BufferedReader br = new BufferedReader(new FileReader(path)); + String line; + try { + line = br.readLine(); + int dim_x = Integer.parseInt(line); + DIMENSION = dim_x; + line = br.readLine(); + int dim_y = Integer.parseInt(line); + rVal = new int[dim_x][dim_y]; + int incrementer_x = 0; + int incrementer_y = 0; + while ((line = br.readLine()) != null) { + incrementer_y = 0; + while(line != ""){ + rVal[incrementer_x][incrementer_y] = Integer.parseInt(Utilities.string_To_First_Space(line)); + if(line.contains(" ")){ + line = line.substring(Utilities.get_Position_Of_Next_Instance_Of_Char_In_String(line, ' ') + 1); + } else { + line = ""; + } + incrementer_y++; + } + incrementer_x++; + } + } catch (IOException ex) { + } + } catch (FileNotFoundException ex) { + } + return rVal; + } + + public static void increment_Current_Continent(){ + current_Continent++; + if (current_Continent > num_Continents - 1) { + current_Continent = 0; + } + while(continents.get(current_Continent).size < 50){ + current_Continent++; + if(current_Continent > num_Continents - 1){ + current_Continent = 0; + } + } + } + + public static void increment_Display_Toggle(){ + display_toggle++; + if(display_toggle > max_Display_Toggle){ + display_toggle = 0; + } + } + + //the interpolation ratio applied to the statically generated terrain + public void setInterpolationRatio(int interpRatio){ + interpolationRatio = interpRatio; + } + + //the interpolation ratio applied to the dynamically generated terrain per chunk + public void setDynamicInterpolationRatio(int dynInterpRatio){ + dynamicInterpRatio = dynInterpRatio; + } + + //Sets the vertical interpolation ratio + public void setVerticalInterpolationRatio(int vertInterpRatio){ + verticalInterpolationRatio = vertInterpRatio; + } + + public void setRandomSeed(long seed){ + Utilities.seed_Random_Functions(seed); + } +} diff --git a/src/main/java/electrosphere/game/terrain/generation/TerrainGenerator.java b/src/main/java/electrosphere/game/terrain/generation/TerrainGenerator.java new file mode 100644 index 00000000..c44b685c --- /dev/null +++ b/src/main/java/electrosphere/game/terrain/generation/TerrainGenerator.java @@ -0,0 +1,314 @@ +package electrosphere.game.terrain.generation; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Random; + +/** + * + * @author satellite + */ +class TerrainGenerator { + static int DIMENSION = 200; + static int[][] asthenosphere_Heat; + static int[][] rock_Hardness; + static int[][] elevation; + static int[][] smoothed_elevation; + static int new_Elevation[][]; + static Vector[][] currents; + static ArrayList spots = new ArrayList(); + static int Time = 0; + static final int ELEVATION_DATA_LENGTH = 50; + static int[] elevation_Data; + static int current_Max_Elevation_Data = 1; + static int lifespan = 75000; + + public TerrainGenerator(){ + + } + + public void set_Dimension(int new_Dim){ + DIMENSION = new_Dim; + } + + public void set_Lifespan(int new_Lifespan){ + lifespan = new_Lifespan; + } + + + public void run(){ + asthenosphere_Heat = new int[DIMENSION][DIMENSION]; + elevation = new int[DIMENSION][DIMENSION]; + smoothed_elevation = new int[DIMENSION][DIMENSION]; + new_Elevation = new int[DIMENSION][DIMENSION]; + currents = new Vector[DIMENSION][DIMENSION]; + for(int x = 0; x < DIMENSION; x++){ + for(int y = 0; y < DIMENSION; y++){ + currents[x][y] = new Vector(); + } + } + elevation_Data = new int[ELEVATION_DATA_LENGTH]; + for(int x = 0; x < ELEVATION_DATA_LENGTH; x++){ + elevation_Data[x] = 0; + } + + + + + long last_Time = System.currentTimeMillis(); + + + while(true){ + Time++; + if(spots.size() >= 1){ + ArrayList to_Remove = new ArrayList(); + Iterator spot_Iterator = spots.iterator(); + while(spot_Iterator.hasNext()){ + Hotspot current_Spot = spot_Iterator.next(); + if(current_Spot.life_current >= current_Spot.life_max){ + to_Remove.add(current_Spot); + } + } + spot_Iterator = to_Remove.iterator(); + while(spot_Iterator.hasNext()){ + Hotspot current_Spot = spot_Iterator.next(); + spots.remove(current_Spot); + } + } + if(spots.size() < 5){ + spots.add(new Hotspot(electrosphere.game.terrain.generation.Utilities.random_Integer(0, DIMENSION - 1),electrosphere.game.terrain.generation.Utilities.random_Integer(0, DIMENSION - 1),electrosphere.game.terrain.generation.Utilities.random_Integer(6000, 10000), electrosphere.game.terrain.generation.Utilities.random_Integer(3, 5), this)); + } + for(int x = 0; x < DIMENSION; x++){ + for(int y = 0; y < DIMENSION; y++){ + asthenosphere_Heat[x][y] = 0; + } + } + if(spots.size() >= 1){ + Iterator spot_Iterator = spots.iterator(); + while(spot_Iterator.hasNext()){ + Hotspot current_Spot = spot_Iterator.next(); + current_Spot.simulate(); + } + } +// try { +// TimeUnit.MILLISECONDS.sleep(1); +// } catch (InterruptedException ex) { +// } + heat_To_Elevation(); + construct_Convection_Cells(); + apply_Vectors_To_Elevation(); + calculate_Smoothed_Elevations(); + if(Time % 500 == 0) { + long new_Time = System.currentTimeMillis(); + long time_Delta = new_Time - last_Time; + last_Time = new_Time; + System.out.println("Progress: " + Time + "/" + lifespan + " ETA: " + (time_Delta * (lifespan - Time) / 1000 / 500) + "S"); + } + if(Time > lifespan){ + break; + } + } + } + + public int[][] get_Terrain(){ + return elevation; + } + + public int[][] get_Terrain_Smoothed(){ + return smoothed_elevation; + } + + static void heat_To_Elevation(){ + for(int x = 0; x < DIMENSION; x++){ + for(int y = 0; y < DIMENSION; y++){ + if(asthenosphere_Heat[x][y] > 25){ + if(electrosphere.game.terrain.generation.Utilities.random_Integer(1, 10) == 10){ + elevation[x][y] = elevation[x][y] + 1; + if(elevation[x][y] > 100){ + elevation[x][y] = 100; + } + } + } + } + } + } + + static void construct_Convection_Cells(){ + boolean is_cell_type_1 = false; + int fourth = DIMENSION / 4; + for(int x = 0; x < DIMENSION; x++){ + for(int y = 0; y < DIMENSION; y++){ + int normalized_x = x; + int normalized_y = y; + if(y < fourth || (y < fourth * 3 && y > (fourth * 2) - 1)){ + is_cell_type_1 = true; + if(normalized_y > fourth){ + normalized_y = normalized_y - fourth * 2; + } + } else { + is_cell_type_1 = false; + if(normalized_y > fourth * 2 + 1){ + normalized_y = normalized_y - fourth * 3; + } else { + normalized_y = normalized_y - fourth; + } + } + while(normalized_x > fourth){ + normalized_x = normalized_x - fourth; + } + if(normalized_x < 0){ + normalized_x = 0; + } + if(normalized_y < 0){ + normalized_y = 0; + } + int eigth = fourth / 2; + normalized_y = normalized_y - eigth; + normalized_x = normalized_x - eigth; + float magnitude = (float)Math.sqrt(Math.pow(normalized_y, 2) + Math.pow(normalized_x, 2)); + if(magnitude < fourth / 10){ + normalized_x = normalized_x + fourth / 10; + magnitude = (float)Math.sqrt(Math.pow(normalized_y, 2) + Math.pow(normalized_x, 2)); + } + double offset_angle = Math.atan2(normalized_y / magnitude, normalized_x / magnitude); + if(offset_angle < 0){ + offset_angle = offset_angle + Math.PI * 2; + } + double vector_angle = offset_angle; + if(is_cell_type_1){ + offset_angle = offset_angle + Math.PI / 2; + } else { + offset_angle = offset_angle - Math.PI / 2; + } + while(offset_angle > Math.PI * 2){ + offset_angle = offset_angle - Math.PI * 2; + } + while(offset_angle < 0){ + offset_angle = offset_angle + Math.PI * 2; + } + currents[x][y].x = (int)(99 * Math.cos(offset_angle)); + currents[x][y].y = (int)(99 * Math.sin(offset_angle)); + } + } + } + + static void apply_Vectors_To_Elevation(){ + int current_Elev[][] = new int[DIMENSION][DIMENSION]; + for(int x = 0; x < DIMENSION; x++){ + for(int y = 0; y < DIMENSION; y++){ + new_Elevation[x][y] = 0; + current_Elev[x][y] = elevation[x][y]; + } + } + for(int x = 0; x < DIMENSION; x++){ + for(int y = 0; y < DIMENSION; y++){ + boolean transfer = false; + if (electrosphere.game.terrain.generation.Utilities.random_Integer(1, 50) == 1) { + transfer = true; + } + int transfer_goal; + if(electrosphere.game.terrain.generation.Utilities.random_Integer(1, 2)==1){ + transfer_goal = electrosphere.game.terrain.generation.Utilities.random_Integer(20, 60); + } else { + transfer_goal = electrosphere.game.terrain.generation.Utilities.random_Integer(0, 60); + } + if(electrosphere.game.terrain.generation.Utilities.random_Integer(1, 2)==1){ + if (currents[x][y].x >= 0) { + if (transfer) { + if (x + 1 < DIMENSION) { + while(new_Elevation[x + 1][y] + current_Elev[x + 1][y] < 99 && current_Elev[x][y] > transfer_goal){ + new_Elevation[x + 1][y]++; + current_Elev[x][y]--; + } + } else { + } + } + } else { + if (transfer) { + if (x - 1 >= 0) { + while(new_Elevation[x - 1][y] + current_Elev[x - 1][y] < 99 && current_Elev[x][y] > transfer_goal){ + new_Elevation[x - 1][y]++; + current_Elev[x][y]--; + } + } else { + } + } + } + } else { + if (currents[x][y].y >= 0) { + if (transfer) { + if (y + 1 < DIMENSION) { // V REPLACE THIS WITH GOAL + while(new_Elevation[x][y + 1] + current_Elev[x][y + 1] < 99 && current_Elev[x][y] > transfer_goal){ + new_Elevation[x][y + 1]++; + current_Elev[x][y]--; + } + } else { + } + } + } else { + if (transfer) { + if (y - 1 >= 0) { + while(new_Elevation[x][y - 1] + current_Elev[x][y - 1] < 99 && current_Elev[x][y] > transfer_goal){ + new_Elevation[x][y - 1]++; + current_Elev[x][y]--; + } + } else { + } + } + } + } + } + } + for(int x = 0; x < DIMENSION; x++){ + for(int y = 0; y < DIMENSION; y++){ + new_Elevation[x][y] = new_Elevation[x][y] + current_Elev[x][y]; + while(new_Elevation[x][y] > 99){ + new_Elevation[x][y] = 99; + } + elevation[x][y] = new_Elevation[x][y]; + } + } + } + + static void grab_Elevation_Data(){ + int new_Elevation_Data_Val = 0; + for(int x = 0; x < DIMENSION; x++){ + for(int y = 0; y < DIMENSION; y++){ + new_Elevation_Data_Val = new_Elevation_Data_Val + elevation[x][y]; + } + } + for(int x = 1; x < ELEVATION_DATA_LENGTH; x++){ + elevation_Data[x-1] = elevation_Data[x]; + } + elevation_Data[ELEVATION_DATA_LENGTH - 1] = new_Elevation_Data_Val; + if(new_Elevation_Data_Val > current_Max_Elevation_Data){ + current_Max_Elevation_Data = new_Elevation_Data_Val; + } + } + + static void calculate_Smoothed_Elevations(){ + int[][] buffer = new int[DIMENSION][DIMENSION]; + for(int x = 1; x < DIMENSION - 2; x++){ + for(int y = 1; y < DIMENSION - 2; y++){ + buffer[x][y] = elevation[x][y] * 4 * elevation[x+1][y] * 2 + elevation[x-1][y] * 2 + elevation[x][y+1] * 2 + + elevation[x][y-1] * 2 + elevation[x+1][y+1] + elevation[x+1][y-1] + elevation[x-1][y+1] + elevation[x-1][y-1]; + buffer[x][y] = (int)(buffer[x][y] / 16.0); + while(buffer[x][y] > 100){ + buffer[x][y] = buffer[x][y]/2; + } + smoothed_elevation[x][y] = buffer[x][y]; + } + } + for(int x = 1; x < DIMENSION - 2; x++){ + for(int y = 1; y < DIMENSION - 2; y++){ + buffer[x][y] = smoothed_elevation[x][y] * 4 * smoothed_elevation[x+1][y] * 2 + smoothed_elevation[x-1][y] * 2 + smoothed_elevation[x][y+1] * 2 + + smoothed_elevation[x][y-1] * 2 + smoothed_elevation[x+1][y+1] + smoothed_elevation[x+1][y-1] + smoothed_elevation[x-1][y+1] + smoothed_elevation[x-1][y-1]; + buffer[x][y] = (int)(buffer[x][y] / 16.0); + while(buffer[x][y] > 100){ + buffer[x][y] = buffer[x][y]/2; + } + smoothed_elevation[x][y] = buffer[x][y]; + } + } + } +} diff --git a/src/main/java/electrosphere/game/terrain/generation/Utilities.java b/src/main/java/electrosphere/game/terrain/generation/Utilities.java new file mode 100644 index 00000000..9c39838c --- /dev/null +++ b/src/main/java/electrosphere/game/terrain/generation/Utilities.java @@ -0,0 +1,260 @@ +package electrosphere.game.terrain.generation; + +import java.awt.Point; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.Random; +import java.util.concurrent.TimeUnit; +/** + * + * @author awhoove + */ +class Utilities { + + static long seed = 0; + + static Random rand = new Random(seed); + + public static void seed_Random_Functions(long seed){ + rand = new Random(seed); + } + + //Finds midpoint of cluster of arbitrary points. + public static Point average_Points(Point[] objects){ + Point rVal = null; + int x = 0; + int y = 0; + for(int i = 0; i < objects.length; i++){ + x = x + objects[i].x; + y = y + objects[i].y; + } + x = x / objects.length; + y = y / objects.length; + rVal = new Point(x,y); + return rVal; + } + public static void sleep(int milliseconds){ + try { + TimeUnit.MILLISECONDS.sleep(milliseconds); + } catch (InterruptedException ex) { + System.out.println("Sleep failed lol."); + } + } + public static int random_Integer(int Min, int Max){ + return Min + (int)(rand.nextDouble() * ((Max - Min) + 1)); + } + public static int random_Range_Distribution_Even(int ... range){ + if(range.length % 2 != 0){ + System.out.println("Invalid number of parameters for range in a function call to \"random_Range_Distribution_Even\"!"); + return -1; + } else { + int total = 0; + int i = 0; + while(i < range.length/2 + 1){ + total = total + range[i+1] - range[i] + 1; + i=i+2; + } + int temp = random_Integer(1,total); + int incrementer = 0; + i = 0; + while(incrementer + range[i+1] - range[i] + 1 < temp){ + incrementer = incrementer + range[i+1] - range[i] + 1; + i=i+2; + } + return range[i] + (temp - incrementer) - 1; + } + } + public static float distance_Between_Points(Point point_One, Point point_Two){ + float rVal = 0.0f; + rVal = (float)Math.sqrt(Math.pow(point_One.y - point_Two.y, 2) + Math.pow(point_One.x - point_Two.x, 2)); + return rVal; + } + public static float angle_Between_Points(Point p1, Point p2){ + float rVal = 0.0f; + rVal = (float)Math.atan2(p1.y - p2.y, p1.x - p2.x); + if(rVal < 0){ + rVal = rVal + (float)(Math.PI * 2); + } + return rVal; + } + + + public static Point centerpoint_Of_Circle_From_Three_Points(Point p1, Point p2, Point p3){ + Point rVal = null; + + final double offset = Math.pow(p2.x, 2) + Math.pow(p2.y, 2); + final double bc = (Math.pow(p1.x, 2) + Math.pow(p1.y, 2) - offset) / 2.0; + final double cd = (offset - Math.pow(p3.x, 2) - Math.pow(p3.y, 2)) / 2.0; + final double det = (p1.x - p2.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p2.y); + if (Math.abs(det) < 0.000001) { + return null; + } + final double idet = 1 / det; + + final double centerx = (bc * (p2.y - p3.y) - cd * (p1.y - p2.y)) * idet; + final double centery = (cd * (p1.x - p2.x) - bc * (p2.x - p3.x)) * idet; + + return new Point((int)centerx,(int)centery); + + /* + float slope_1 = (p2.y - p1.y) / (p2.x - p1.x); + float slope_2 = (p3.y - p2.y) / (p3.x - p2.x); + //http://paulbourke.net/geometry/circlesphere/ + float intersect_X = (slope_1*slope_2*(p1.y-p3.y)+slope_2*(p1.x+p2.x)-slope_1*(p2.x+p3.x))/(2*(slope_2-slope_1)); + float intersect_Y = -(1/slope_1)*(intersect_X - (p2.x+p3.x)/2)+(p2.y+p3.y)/2; + rVal = new Point((int)intersect_X, (int)intersect_Y); + return rVal; + */ + } + public static String pull_Random_String_From_File(File source){ + String rVal = ""; + int line_To_Go_To; + try { + BufferedReader reader = new BufferedReader(new FileReader(source)); + line_To_Go_To = random_Integer(1,Integer.parseInt(reader.readLine())); + int i = 0; + while(i 5000){ + break; + } + } + rVal = reader.readLine(); + } catch (FileNotFoundException ex) { + System.out.println("Utilities/pull_Random_String_From_File failed to read from "+source+" (File not found)."); + } catch (IOException ex){ + System.out.println("Utilities/pull_Random_String_From_File failed to read from "+source+" (IOException)."); + } + return rVal; + } + + public static String pull_Ordered_String_From_File(File source, int dest){ + String rVal = ""; + try { + BufferedReader reader = new BufferedReader(new FileReader(source)); + int i = 0; + while(i0){ + itterator--; + } else { + rVal = i; + break; + } + } + } + return rVal; + } + + public static float[] linearize_Float_Array_Column_Major(float[][] in){ + float[] rVal = new float[in[0].length * in.length]; + for(int x = 0; x < in.length; x++){ + for(int y = 0; y < in[0].length; y++){ + rVal[x*in[0].length + y] = in[x][y]; + } + } + return rVal; + } + public static void print_Linearized_Array(float[] in){ + for(int i = 0; i < in.length; i++){ + System.out.println(in[i]); + } + } + public static float[] linearize_Float_Array_Row_Major(float[][] in){ + float[] rVal = new float[in[0].length * in.length]; + for(int y = 0; y < in[0].length; y++){ + for(int x = 0; x < in.length; x++){ + rVal[x*in[0].length + y] = in[x][y]; + } + } + return rVal; + } + public static String string_To_First_Space(String s){ + String rVal = null; + for(int i = 0; i < s.length(); i++){ + if(s.charAt(i)==' '){ + rVal = s.substring(0, i); + break; + } + } + if(rVal == null){ + rVal = s; + } + return rVal; + } + public static int position_To_First_Space(String s){ + int rVal = -1; + for(int i = 0; i < s.length(); i++){ + if(s.charAt(i)==' '){ + rVal = i; + break; + } + } + return rVal; + } + public static int number_Of_Spaces_In_String(String s){ + int rVal = 0; + for(int i = 0; i < s.length(); i++){ + if(s.charAt(i)==' '){ + rVal++; + } + } + return rVal; + } + public static String get_Face_From_OBJ_Format(String s){ + String rVal = ""; + int counter = 0; + for(int i = 0; i < s.length(); i++){ + counter = i; + if(s.charAt(i)=='/'){ + break; + } + } + if(counter+1 == s.length()){ + counter++; + } + rVal = s.substring(0, counter); + return rVal; + } +} diff --git a/src/main/java/electrosphere/game/terrain/generation/Vector.java b/src/main/java/electrosphere/game/terrain/generation/Vector.java new file mode 100644 index 00000000..94b9c3d5 --- /dev/null +++ b/src/main/java/electrosphere/game/terrain/generation/Vector.java @@ -0,0 +1,10 @@ +package electrosphere.game.terrain.generation; + +/** + * + * @author satellite + */ +class Vector { + public int x; + public int y; +} diff --git a/src/main/java/electrosphere/game/terrain/generation/models/TerrainModel.java b/src/main/java/electrosphere/game/terrain/generation/models/TerrainModel.java new file mode 100644 index 00000000..3a0bdae3 --- /dev/null +++ b/src/main/java/electrosphere/game/terrain/generation/models/TerrainModel.java @@ -0,0 +1,694 @@ +package electrosphere.game.terrain.generation.models; + +import java.util.Random; + +public class TerrainModel { + + int dynamicInterpolationRatio; + + int discreteArrayDimension; + + float[][] elevation; + + long[][] chunkRandomizer; + + float randomDampener = 0.4f; + + TerrainModel() { + + } + + public TerrainModel(int dimension, float[][] elevation, long[][] chunkRandomizer, int dynamicInterpolationRatio){ + + this.dynamicInterpolationRatio = dynamicInterpolationRatio; + this.discreteArrayDimension = dimension; + this.elevation = elevation; + this.chunkRandomizer = chunkRandomizer; + + } + + public static TerrainModel constructTerrainModel(int dimension, int dynamicInterpolationRatio){ + TerrainModel rVal = new TerrainModel(); + rVal.discreteArrayDimension = dimension; + rVal.dynamicInterpolationRatio = dynamicInterpolationRatio; + return rVal; + } + + public float[][] getElevation(){ + return elevation; + } + + public void setRandomDampener(float f){ + randomDampener = f; + } + + /** + * Dynamically interpolates a chunk of a specific size from the pre-existing elevation map + * @param x The x position on the elevation map to get a chunk from + * @param y The y position on the elevation map to get a chunk from + * @return Dynamically interpolated float array of elevations of chunk + */ + public float[][] getElevationForChunk(int x, int y){ + + Random rand = new Random(chunkRandomizer[x][y]); + //this is what we intend to return from the function + float[][] rVal = new float[dynamicInterpolationRatio][dynamicInterpolationRatio]; + + /* + So we're looking at chunk x,y + + if this is our grid: + + 4 0.1 0.2 0.3 0.4 + ^ + | 3 0.1 0.2 0.3 0.4 + | + | 2 0.1 0.2 0.3 0.4 + + x 1 0.1 0.2 0.3 0.4 + + 0 1 2 3 + + y ---- > + + say we're looking at x=2,y=1 + + "macroValues" should contain the values for bounds x = [1,3] and y = [0,2] + + the goal is to have the "center" of the output chunk have the value the + elevation grid at x=2,y=1 + + */ + + + //set macroValues + float[][] macroValues = getMacroValuesAtPosition(x,y); + + + + + int halfLength = dynamicInterpolationRatio/2; + + /* + + Four quadrants we're generating + + _____________________ + |1 |2 | + | | | + | | | + | | | + |__________|__________| + |3 |4 | + | | | + | | | + |__________|__________| + + First set of loops is quadrant 1 + then quadrant 2 + then quadrant 3 + then quadrant 4 + + */ + + int outXOffset = 0; + int outYOffset = 0; + + for(int i = 0; i < halfLength; i++){ + for(int j = 0; j < halfLength; j++){ + rVal[i+outXOffset][j+outYOffset] = + (1.0f * (halfLength - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[0][0] + + (1.0f * (0 - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[1][0] + + (1.0f * (halfLength - i) * (0 - j))/(halfLength * halfLength) * macroValues[0][1] + + (1.0f * (0 - i) * (0 - j))/(halfLength * halfLength) * macroValues[1][1] + ; + } + } + + outXOffset = halfLength; + for(int i = 0; i < halfLength; i++){ + for(int j = 0; j < halfLength; j++){ + rVal[i+outXOffset][j+outYOffset] = + (1.0f * (halfLength - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[1][0] + + (1.0f * (0 - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[2][0] + + (1.0f * (halfLength - i) * (0 - j))/(halfLength * halfLength) * macroValues[1][1] + + (1.0f * (0 - i) * (0 - j))/(halfLength * halfLength) * macroValues[2][1] + ; + } + } + + outXOffset = 0; + outYOffset = halfLength; + for(int i = 0; i < halfLength; i++){ + for(int j = 0; j < halfLength; j++){ + rVal[i+outXOffset][j+outYOffset] = + (1.0f * (halfLength - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[0][1] + + (1.0f * (0 - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[1][1] + + (1.0f * (halfLength - i) * (0 - j))/(halfLength * halfLength) * macroValues[0][2] + + (1.0f * (0 - i) * (0 - j))/(halfLength * halfLength) * macroValues[1][2] + ; + } + } + + outXOffset = halfLength; + for(int i = 0; i < halfLength; i++){ + for(int j = 0; j < halfLength; j++){ + rVal[i+outXOffset][j+outYOffset] = + (1.0f * (halfLength - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[1][1] + + (1.0f * (0 - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[2][1] + + (1.0f * (halfLength - i) * (0 - j))/(halfLength * halfLength) * macroValues[1][2] + + (1.0f * (0 - i) * (0 - j))/(halfLength * halfLength) * macroValues[2][2] + ; + } + } + + + return rVal; + } + + + /* + !!!ASSUMPTIONS!!! + square matricies of data + object has already been initialized + dynamicInterpolationRatio is set to some valid power of 2 + */ + + /** + * Dynamically interpolates a chunk of a specific size from the pre-existing elevation map + * Includes the boundary of the chunk as well + * @param x The x position on the elevation map to get a chunk from + * @param y The y position on the elevation map to get a chunk from + * @return Dynamically interpolated float array of elevations of chunk + */ + public float[][] getAugmentedElevationForChunk(float[][] macroValues, long[][] randomizerValues){ + + //this is what we intend to return from the function + float[][] rVal = new float[dynamicInterpolationRatio+1][dynamicInterpolationRatio+1]; + + /* + So we're looking at chunk x,y + + if this is our grid: + + 4 0.1 0.2 0.3 0.4 + ^ + | 3 0.1 0.2 0.3 0.4 + | + | 2 0.1 0.2 0.3 0.4 + + x 1 0.1 0.2 0.3 0.4 + + 0 1 2 3 + + y ---- > + + say we're looking at x=2,y=1 + + "macroValues" should contain the values for x = [1,3] and y = [0,2] + + the goal is to have the "center" of the output chunk have the value the + elevation grid at x=2,y=1 + + */ + + + + /* + + Our "macro values" at the edges of this chunk need to be haldway between this chunk's center height value and the next + Why? think about it for a second + + + If we have some grid with chunks a and b + + 0.1 0.2 0.3 0.8 + 0.4 a b 0.9 + 0.5 0.6 0.7 1.0 + + + to generate a chunk at point a that smoothly merges into chunk b, you need + the chunk at a to know the value of b + + but you don't want the right edge of the chunk at a to be the VALUE of b + because then it would work + + so if we have two chunks + + _____________________ _____________________ + | | || | | + | | || | | + | | || | | + | | || | | + |__________a__________**__________b__________| + | | || | | + | | || | | + | | || | | + |__________|__________||__________|__________| + + you want the value at ** to be half way between a and b + + the corner cases are even worse, you have to average all four + _____________________ _____________________ + | | || | | + | | || | | + | | || | | + | | || | | + |__________a__________||__________b__________| + | | || | | + | | || | | + | | || | | + |__________|__________**__________|__________| + _____________________**_____________________ + | | || | | + | | || | | + | | || | | + | | || | | + |__________c__________||__________d__________| + | | || | | + | | || | | + | | || | | + |__________|__________||__________|__________| + + + + */ + + float[][] newMacroValues = new float[3][3]; + newMacroValues[0][0] = (macroValues[0][0] + macroValues[0][1] + macroValues[1][0] + macroValues[1][1]) / 4.0f; + newMacroValues[2][0] = (macroValues[2][0] + macroValues[2][1] + macroValues[1][0] + macroValues[1][1]) / 4.0f; + newMacroValues[0][2] = (macroValues[0][2] + macroValues[0][1] + macroValues[1][2] + macroValues[1][1]) / 4.0f; + newMacroValues[2][2] = (macroValues[2][2] + macroValues[2][1] + macroValues[1][2] + macroValues[1][1]) / 4.0f; + newMacroValues[1][0] = (macroValues[1][0] + macroValues[1][1]) / 2.0f; + newMacroValues[0][1] = (macroValues[0][1] + macroValues[1][1]) / 2.0f; + newMacroValues[1][2] = (macroValues[1][2] + macroValues[1][1]) / 2.0f; + newMacroValues[2][1] = (macroValues[2][1] + macroValues[1][1]) / 2.0f; + newMacroValues[1][1] = macroValues[1][1]; + + macroValues = newMacroValues; + + + + + + +// System.out.println("discreteArrayDimension: " + discreteArrayDimension); + + + + //The following values + random seed are what are primarily used for calculating elevation map + //If you want to transmit the output of this model without transmitting the entire field + //transmit these numbers + seeds + +// System.out.println("Macro values for " + x + "," + y + "\n"); +// System.out.println(macroValues[0][0] + " " + macroValues[1][0] + " " + macroValues[2][0]); +// System.out.println(macroValues[0][1] + " " + macroValues[1][1] + " " + macroValues[2][1]); +// System.out.println(macroValues[0][2] + " " + macroValues[1][2] + " " + macroValues[2][2]); +// System.out.println(); + + + int halfLength = dynamicInterpolationRatio/2; + + /* + + Four quadrants we're generating + + _____________________ + |1 |2 | + | | | + | | | + | | | + |__________|__________| + |3 |4 | + | | | + | | | + |__________|__________| + + First set of loops is quadrant 1 + then quadrant 2 + then quadrant 3 + then quadrant 4 + + + 0,0 1,0 2,0 + _____________________ + |1 |2 | + | | | + | | | + | | | + 0,1 |__________|__________| 2,1 + |3 |4 | + | | | + | | | + |__________|__________| + 0,2 1,2 2,2 + + Where the "macro values" correspond to on our array + + */ + + int outXOffset = 0; + int outYOffset = 0; + + long quadrantRandom = + randomizerValues[0][0] + + randomizerValues[1][0] + + randomizerValues[0][1] + + randomizerValues[1][1]; + + Random quadRand = new Random(quadrantRandom); + + for(int i = 0; i < halfLength; i++){ + for(int j = 0; j < halfLength; j++){ + rVal[i+outXOffset][j+outYOffset] = + (1.0f * (halfLength - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[0][0] + + (1.0f * (i - 0) * (halfLength - j))/(halfLength * halfLength) * macroValues[1][0] + + (1.0f * (halfLength - i) * (j - 0))/(halfLength * halfLength) * macroValues[0][1] + + (1.0f * (i - 0) * (j - 0))/(halfLength * halfLength) * macroValues[1][1] + + quadRand.nextFloat() * randomDampener + ; + } + } + + + quadrantRandom = + randomizerValues[1][0] + + randomizerValues[2][0] + + randomizerValues[1][1] + + randomizerValues[2][1]; + + quadRand = new Random(quadrantRandom); + + outXOffset = halfLength; + for(int i = 0; i < halfLength; i++){ + for(int j = 0; j < halfLength; j++){ + rVal[i+outXOffset][j+outYOffset] = + (1.0f * (halfLength - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[1][0] + + (1.0f * (i - 0) * (halfLength - j))/(halfLength * halfLength) * macroValues[2][0] + + (1.0f * (halfLength - i) * (j - 0))/(halfLength * halfLength) * macroValues[1][1] + + (1.0f * (i - 0) * (j - 0))/(halfLength * halfLength) * macroValues[2][1] + + quadRand.nextFloat() * randomDampener + ; + } + } + + + + quadrantRandom = + randomizerValues[0][1] + + randomizerValues[1][1] + + randomizerValues[0][2] + + randomizerValues[1][2]; + + quadRand = new Random(quadrantRandom); + + outXOffset = 0; + outYOffset = halfLength; + for(int i = 0; i < halfLength; i++){ + for(int j = 0; j < halfLength; j++){ + rVal[i+outXOffset][j+outYOffset] = + (1.0f * (halfLength - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[0][1] + + (1.0f * (i - 0) * (halfLength - j))/(halfLength * halfLength) * macroValues[1][1] + + (1.0f * (halfLength - i) * (j - 0))/(halfLength * halfLength) * macroValues[0][2] + + (1.0f * (i - 0) * (j - 0))/(halfLength * halfLength) * macroValues[1][2] + + quadRand.nextFloat() * randomDampener + ; + } + } + + + quadrantRandom = + randomizerValues[1][1] + + randomizerValues[2][1] + + randomizerValues[1][2] + + randomizerValues[2][2]; + + quadRand = new Random(quadrantRandom); + + outXOffset = halfLength; + for(int i = 0; i < halfLength; i++){ + for(int j = 0; j < halfLength; j++){ + rVal[i+outXOffset][j+outYOffset] = + (1.0f * (halfLength - i) * (halfLength - j))/(halfLength * halfLength) * macroValues[1][1] + + (1.0f * (i - 0) * (halfLength - j))/(halfLength * halfLength) * macroValues[2][1] + + (1.0f * (halfLength - i) * (j - 0))/(halfLength * halfLength) * macroValues[1][2] + + (1.0f * (i - 0) * (j - 0))/(halfLength * halfLength) * macroValues[2][2] + + quadRand.nextFloat() * randomDampener + ; + } + } + + + /* + + Four quadrants we're generating + + ------------> + _____________________ + | |1 |2 | + | | | | | + | | | | | + | | | | | + | |__________|__________| | + | |3 |4 | | + | | | | | + V | | | V + |__________|__________| + + ----------> + + filling in along these arrows now + + + + this is the "order" of the edges: + + + 1 2 + + 3 5 + + + 4 6 + + 7 8 + + + the corresponding randoms for each edge are + + 1 + 2 3 + 4 + + */ + + Random edge1Rand = new Random(randomizerValues[1][1] + randomizerValues[1][0]); + Random edge2Rand = new Random(randomizerValues[1][1] + randomizerValues[0][1]); + Random edge3Rand = new Random(randomizerValues[1][1] + randomizerValues[2][1]); + Random edge4Rand = new Random(randomizerValues[1][1] + randomizerValues[1][2]); + + + + for(int i = 0; i < halfLength; i++){ + //edge 1 + rVal[i][0] = + (1.0f * (halfLength - i) / halfLength) * macroValues[0][0] + + (1.0f * (i - 0) / halfLength) * macroValues[1][0] + + edge1Rand.nextFloat() * randomDampener + ; + + + //edge 2 + rVal[i+halfLength][0] = + (1.0f * (halfLength - i) / halfLength) * macroValues[1][0] + + (1.0f * (i - 0) / halfLength) * macroValues[2][0] + + edge1Rand.nextFloat() * randomDampener + ; + + + //edge 3 + rVal[0][i] = + (1.0f * (halfLength - i) / halfLength) * macroValues[0][0] + + (1.0f * (i - 0) / halfLength) * macroValues[0][1] + + edge2Rand.nextFloat() * randomDampener + ; + + + //edge 4 + rVal[0][i+halfLength] = + (1.0f * (halfLength - i) / halfLength) * macroValues[0][1] + + (1.0f * (i - 0) / halfLength) * macroValues[0][2] + + edge2Rand.nextFloat() * randomDampener + ; + + //edge 5 + rVal[dynamicInterpolationRatio][i] = + (1.0f * (halfLength - i) / halfLength) * macroValues[2][0] + + (1.0f * (i - 0) / halfLength) * macroValues[2][1] + + edge3Rand.nextFloat() * randomDampener + ; + + + //edge 6 + rVal[dynamicInterpolationRatio][i+halfLength] = + (1.0f * (halfLength - i) / halfLength) * macroValues[2][1] + + (1.0f * (i - 0) / halfLength) * macroValues[2][2] + + edge3Rand.nextFloat() * randomDampener + ; + + + //edge 7 + rVal[i][dynamicInterpolationRatio] = + (1.0f * (halfLength - i) / halfLength) * macroValues[0][2] + + (1.0f * (i - 0) / halfLength) * macroValues[1][2] + + edge4Rand.nextFloat() * randomDampener + ; + + + //edge 8 + rVal[i+halfLength][dynamicInterpolationRatio] = + (1.0f * (halfLength - i) / halfLength) * macroValues[1][2] + + (1.0f * (i - 0) / halfLength) * macroValues[2][2] + + edge4Rand.nextFloat() * randomDampener + ; + } + + //"Because it is passed over twice" this doesn't get the right value after the loops + //so we do it manually to correct +// rVal[0][dynamicInterpolationRatio] = +// macroValues[0][2] + +// new Random(randomizerValues[0][2] + randomizerValues[1][2]).nextFloat() +// ; + + + //"Because array dimensions" this doesn't end up getting filled in + //so we have to do it manually + //set final corner at bottom right + rVal[dynamicInterpolationRatio][dynamicInterpolationRatio] = + macroValues[2][2] + + new Random(randomizerValues[1][2] + randomizerValues[2][2]).nextFloat() * randomDampener + ; + + + return rVal; + } + + + + /* + So we're looking at chunk x,y + + if this is our grid: + + 4 0.1 0.2 0.3 0.4 + ^ + | 3 0.1 0.2 0.3 0.4 + | + | 2 0.1 0.2 0.3 0.4 + + x 1 0.1 0.2 0.3 0.4 + + 0 1 2 3 + + y ---- > + + say we're looking at x=2,y=1 + + "macroValues" should contain the values for x = [1,3] and y = [0,2] + + the goal is to have the "center" of the output chunk have the value the + elevation grid at x=2,y=1 + + */ + + public float[][] getMacroValuesAtPosition(int x, int y){ + float[][] rVal = new float[3][3]; + rVal[1][1] = elevation[x][y]; + if(x - 1 >= 0){ + rVal[0][1] = elevation[x-1][y]; + if(y - 1 >= 0){ + rVal[0][0] = elevation[x-1][y-1]; + } + if(y + 1 < discreteArrayDimension){ + rVal[0][2] = elevation[x-1][y+1]; + } + } + if(x + 1 < discreteArrayDimension){ + rVal[2][1] = elevation[x+1][y]; + if(y - 1 >= 0){ + rVal[2][0] = elevation[x+1][y-1]; + } + if(y + 1 < discreteArrayDimension){ + rVal[2][2] = elevation[x+1][y+1]; + } + } + if(y - 1 >= 0){ + rVal[1][0] = elevation[x][y-1]; + } + if(y + 1 < discreteArrayDimension){ + rVal[1][2] = elevation[x][y+1]; + } + return rVal; + } + + + + + + /* + So we're looking at chunk x,y + + if this is our grid: + + 4 0.1 0.2 0.3 0.4 + ^ + | 3 0.1 0.2 0.3 0.4 + | + | 2 0.1 0.2 0.3 0.4 + + x 1 0.1 0.2 0.3 0.4 + + 0 1 2 3 + + y ---- > + + say we're looking at x=2,y=1 + + "macroValues" should contain the values for x = [1,3] and y = [0,2] + + the goal is to have the "center" of the output chunk have the value the + elevation grid at x=2,y=1 + + */ + + public long[][] getRandomizerValuesAtPosition(int x, int y){ + long[][] rVal = new long[3][3]; + rVal[1][1] = chunkRandomizer[x][y]; + if(x - 1 >= 0){ + rVal[0][1] = chunkRandomizer[x-1][y]; + if(y - 1 >= 0){ + rVal[0][0] = chunkRandomizer[x-1][y-1]; + } + if(y + 1 < discreteArrayDimension){ + rVal[0][2] = chunkRandomizer[x-1][y+1]; + } + } + if(x + 1 < discreteArrayDimension){ + rVal[2][1] = chunkRandomizer[x+1][y]; + if(y - 1 >= 0){ + rVal[2][0] = chunkRandomizer[x+1][y-1]; + } + if(y + 1 < discreteArrayDimension){ + rVal[2][2] = chunkRandomizer[x+1][y+1]; + } + } + if(y - 1 >= 0){ + rVal[1][0] = chunkRandomizer[x][y-1]; + } + if(y + 1 < discreteArrayDimension){ + rVal[1][2] = chunkRandomizer[x][y+1]; + } + return rVal; + } + +} diff --git a/src/main/java/electrosphere/main/Globals.java b/src/main/java/electrosphere/main/Globals.java index ee8d9592..1257bb86 100644 --- a/src/main/java/electrosphere/main/Globals.java +++ b/src/main/java/electrosphere/main/Globals.java @@ -27,6 +27,7 @@ import electrosphere.net.client.ClientNetworking; import electrosphere.net.server.Server; import electrosphere.renderer.ModelUtils; import electrosphere.renderer.RenderUtils; +import electrosphere.renderer.RenderingEngine; import electrosphere.renderer.ShaderProgram; import electrosphere.renderer.assetmanager.AssetDataStrings; import electrosphere.renderer.assetmanager.AssetManager; @@ -61,6 +62,11 @@ public class Globals { public static MainConfig mainConfig; + // + //Rendering Engine + // + public static RenderingEngine renderingEngine; + // //Client connection to server diff --git a/src/main/java/electrosphere/main/Main.java b/src/main/java/electrosphere/main/Main.java index f75bd4d5..98a6498e 100644 --- a/src/main/java/electrosphere/main/Main.java +++ b/src/main/java/electrosphere/main/Main.java @@ -5,14 +5,10 @@ import electrosphere.cfg.MainConfig; import electrosphere.controls.ControlHandler; import electrosphere.entity.CameraEntityUtils; import electrosphere.entity.types.creature.CreatureUtils; -import electrosphere.renderer.Camera; import electrosphere.renderer.Material; import electrosphere.renderer.Model; import electrosphere.renderer.RenderUtils; -import electrosphere.renderer.ShaderProgram; -import electrosphere.renderer.texture.Texture; import electrosphere.entity.Entity; -import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityUtils; import electrosphere.entity.state.MovementTree; import electrosphere.entity.types.hitbox.HitboxUtils; @@ -22,17 +18,7 @@ import electrosphere.game.state.AttachUtils; import electrosphere.game.state.LoadingThread; import electrosphere.game.state.SimulationState; import electrosphere.game.state.SimulationState.SimulationStateMachine; -import electrosphere.game.terrain.TerrainManager; -import electrosphere.net.client.ClientNetworkMessage; -import electrosphere.net.client.ClientNetworking; -import electrosphere.net.server.Server; -import electrosphere.renderer.Actor; -import electrosphere.renderer.ui.font.FontUtils; -import electrosphere.renderer.ModelUtils; -import electrosphere.renderer.ui.WidgetUtils; -import electrosphere.renderer.ui.font.TextBox; -import electrosphere.terraingen.TerrainGen; -import electrosphere.terraingen.models.TerrainModel; +import electrosphere.renderer.RenderingEngine; import java.util.ArrayList; import java.util.Iterator; import java.util.Random; @@ -42,14 +28,6 @@ import org.joml.Quaternionf; import org.joml.Vector3f; import org.lwjgl.glfw.*; import static org.lwjgl.glfw.GLFW.*; -import org.lwjgl.opengl.*; -import static org.lwjgl.opengl.GL11.*; -import static org.lwjgl.opengl.GL13.*; -import static org.lwjgl.opengl.GL14.*; -import static org.lwjgl.opengl.GL15.*; -import static org.lwjgl.opengl.GL20.*; -import static org.lwjgl.opengl.GL30.*; -import static org.lwjgl.system.MemoryUtil.NULL; import electrosphere.util.ModelLoader; import electrosphere.util.Utilities; import java.io.File; @@ -62,10 +40,7 @@ import java.util.logging.Level; import java.util.logging.Logger; -/** - * - * @author amaterasu - */ + public class Main { @@ -138,7 +113,8 @@ public class Main { Globals.initGlobals(); //create the drawing context - RenderUtils.createOpenglContext(); + Globals.renderingEngine = new RenderingEngine(); + Globals.renderingEngine.createOpenglContext(); //init default resources Globals.initDefaultGraphicalResources(); @@ -274,7 +250,7 @@ public class Main { - RenderUtils.drawScreen(); + Globals.renderingEngine.drawScreen(); if(glfwWindowShouldClose(Globals.window)){ diff --git a/src/main/java/electrosphere/renderer/RenderUtils.java b/src/main/java/electrosphere/renderer/RenderUtils.java index 59ba99ec..7dc12bc4 100644 --- a/src/main/java/electrosphere/renderer/RenderUtils.java +++ b/src/main/java/electrosphere/renderer/RenderUtils.java @@ -115,17 +115,7 @@ import static org.lwjgl.system.MemoryUtil.NULL; */ public class RenderUtils { - public static final int GL_DEFAULT_FRAMEBUFFER = 0; - public static final int GL_DEFAULT_RENDERBUFFER = 0; - static Framebuffer screenFramebuffer; - static Renderbuffer screenRenderbuffer; - static int screenTextureVAO; - static ShaderProgram screenTextureShaders; - static ShaderProgram lightDepthShaderProgram; - static Framebuffer lightDepthBuffer; - - public static boolean renderHitboxes = true; static int createScreenTextureVAO(){ int rVal = glGenVertexArrays(); @@ -189,71 +179,7 @@ public class RenderUtils { return rVal; } - public static void createOpenglContext(){ - //Sets the variables that control the window sizing - int screenWidth = Globals.WINDOW_WIDTH; - int screenHeight = Globals.WINDOW_HEIGHT; - //Initializes opengl - glfwInit(); - //Gives hints to glfw to control how opengl will be used - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); -// glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); Allows you to make the background transparent -// glfwWindowHint(GLFW_OPACITY, 23); - //Creates the window reference object - Globals.window = glfwCreateWindow(screenWidth, screenHeight, "LearnOpenGL", NULL, NULL); - //Errors for failure to create window (IE: No GUI mode on linux ?) - if (Globals.window == NULL) { - System.out.println("Failed to make window."); - glfwTerminate(); - } - //Makes the window that was just created the current OS-level window context - glfwMakeContextCurrent(Globals.window); - //Maximize it - glfwMaximizeWindow(Globals.window); - //Creates the OpenGL capabilities for the program. - GL.createCapabilities(); - - //This enables Z-buffering so that farther-back polygons are not drawn over nearer ones - glEnable(GL_DEPTH_TEST); - - // Support for transparency - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - //Hide the cursor and capture it - glfwSetInputMode(Globals.window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); - - - - //init screen rendering quadrant - screenTextureVAO = createScreenTextureVAO(); -// initScreenTextureShaderProgram(); - screenTextureShaders = ShaderProgram.loadSpecificShader("/Shaders/screentexture/simple1/simple1.vs", "/Shaders/screentexture/simple1/simple1.fs"); -// screenTextureShaders = ShaderProgram.loadSpecificShader("/Shaders/screentexture/drawDepthBuffer/drawDepthBuffer.vs", "/Shaders/screentexture/drawDepthBuffer/drawDepthBuffer.fs"); - - //generate framebuffers - screenFramebuffer = FramebufferUtils.generateScreensizeTextureFramebuffer(); - glBindFramebuffer(GL_FRAMEBUFFER, GL_DEFAULT_FRAMEBUFFER); - glBindRenderbuffer(GL_RENDERBUFFER, GL_DEFAULT_RENDERBUFFER); - - lightDepthShaderProgram = ShaderProgram.loadSpecificShader("/Shaders/lightDepth/lightDepth.vs", "/Shaders/lightDepth/lightDepth.fs"); - Globals.depthMapShaderProgramLoc = lightDepthShaderProgram.shaderProgram; - lightDepthBuffer = FramebufferUtils.generateDepthBuffer(); - Globals.shadowMapTextureLoc = lightDepthBuffer.getTexture(); -// glEnable(GL_CULL_FACE); // enabled for shadow mapping - - - // - // Projection and View matrix creation - // - Globals.projectionMatrix = new Matrix4f(); - Globals.viewMatrix = new Matrix4f(); - float FOV = (float)(120.0f * Math.PI /180.0f); - Globals.projectionMatrix.setPerspective(FOV, 1.0f, 0.1f, view_Range); - Globals.viewMatrix.translation(new Vector3f(0.0f,0.0f,-3.0f)); - } + public static void recaptureScreen(){ @@ -506,266 +432,6 @@ public class RenderUtils { } - public static void drawScreen(){ - - // - //first pass: generate depth map - // - if(Globals.RENDER_FLAG_RENDER_SHADOW_MAP){ - renderShadowMapFramebufferContent(); - } - - - /* - Render content to the game framebuffer - */ - if(Globals.RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER_CONTENT){ - renderGameFramebufferContent(); - } - - - - - //bind default FBO - glBindFramebuffer(GL_FRAMEBUFFER,0); - glDisable(GL_DEPTH_TEST); - - glClearColor(1.0f, 1.0f, 1.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - - /* - Render the game framebuffer texture to a quad - */ - if(Globals.RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER){ - renderScreenFramebuffer(); - } - - /* - Render black background - */ - if(Globals.RENDER_FLAG_RENDER_BLACK_BACKGROUND){ - renderBlackBackground(); - } - - /* - Render any ui elements - */ - if(Globals.RENDER_FLAG_RENDER_UI){ - renderUI(); - } - - - - - - - //check and call events and swap the buffers - glfwSwapBuffers(Globals.window); - glfwPollEvents(); - } - static void renderShadowMapFramebufferContent(){ - - Matrix4f modelTransformMatrix = new Matrix4f(); - - //set the viewport to shadow map size - glViewport(0, 0, 4096, 4096); - glEnable(GL_DEPTH_TEST); - lightDepthBuffer.bind(); - glClear(GL_DEPTH_BUFFER_BIT); - - //set matrices for light render - float near_plane = 0.001f, far_plane = 100.5f; - Matrix4f lightProjection = new Matrix4f().setOrtho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane);//glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane); - Matrix4f lightView = new Matrix4f().lookAt( - new Vector3f(-20.0f, 40.0f, -10.0f), - new Vector3f( 0.0f, 0.0f, 0.0f), - new Vector3f( 0.0f, 1.0f, 0.0f) - ); - Globals.lightDepthMatrix = new Matrix4f(lightProjection).mul(lightView); - - glUseProgram(lightDepthShaderProgram.shaderProgram); - glUniformMatrix4fv(glGetUniformLocation(lightDepthShaderProgram.shaderProgram, "lightSpaceMatrix"), false, Globals.lightDepthMatrix.get(new float[16])); - -// glCullFace(GL_FRONT); - - // - // D R A W A L L E N T I T I E S - // - modelTransformMatrix = new Matrix4f(); - for(Entity currentEntity : Globals.entityManager.getDrawable()){ - //fetch actor - Actor currentActor = EntityUtils.getEntityActor(currentEntity); - //increment animations - if(currentActor.getCurrentAnimation() != null){ - currentActor.incrementAnimationTime(deltaTime * 500); - } - //calculate and apply model transform - modelTransformMatrix.identity(); - modelTransformMatrix.translate(new Vector3f(EntityUtils.getEntityPosition(currentEntity)).sub(CameraEntityUtils.getCameraCenter(Globals.playerCamera)).sub(new Vector3f(0,1,0))); - modelTransformMatrix.rotate(EntityUtils.getEntityRotation(currentEntity)); - modelTransformMatrix.scale(EntityUtils.getEntityScale(currentEntity)); - currentActor.applyModelMatrix(modelTransformMatrix); - //draw - currentActor.drawForDepthBuffer(); - } - - - - //bind default framebuffer - glBindFramebuffer(GL_FRAMEBUFFER,0); - //reset texture - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, 0); - //reset the viewport to screen size - glViewport(0, 0, Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT); - //resume culling backface -// glCullFace(GL_BACK); - } - - - static void renderGameFramebufferContent(){ - - Matrix4f modelTransformMatrix = new Matrix4f(); - - //bind screen fbo - screenFramebuffer.bind(); - glEnable(GL_DEPTH_TEST); - - /// - /// R E N D E R I N G S T U F F - /// - //Sets the background color. - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - - // - // D R A W A L L E N T I T I E S - // - modelTransformMatrix = new Matrix4f(); - for(Entity currentEntity : Globals.entityManager.getDrawable()){ - //fetch actor - Actor currentActor = EntityUtils.getEntityActor(currentEntity); - //increment animations - //this is incremented in the shadow calculations -// if(currentActor.getCurrentAnimation() != null){ -// currentActor.incrementAnimationTime(deltaTime * 500); -// } - //calculate and apply model transform - modelTransformMatrix.identity(); - modelTransformMatrix.translate(new Vector3f(EntityUtils.getEntityPosition(currentEntity)).sub(CameraEntityUtils.getCameraCenter(Globals.playerCamera)).sub(new Vector3f(0,1,0))); - modelTransformMatrix.rotate(EntityUtils.getEntityRotation(currentEntity)); - modelTransformMatrix.scale(EntityUtils.getEntityScale(currentEntity)); - currentActor.applyModelMatrix(modelTransformMatrix); - //draw - currentActor.draw(); - -// Model currentModel = Globals.assetManager.fetchModel(EntityUtils.getEntityModelPath(currentEntity)); -// if(currentModel != null){ -// if(currentModel.currentAnimation != null){ -// currentModel.incrementTime(deltaTime * 500); -// } -// currentModel.modelMatrix = new Matrix4f(); -// currentModel.modelMatrix.translate(new Vector3f(EntityUtils.getEntityPosition(currentEntity)).sub(CameraEntityUtils.getCameraCenter(Globals.playerCamera)).sub(new Vector3f(0,1,0))); -// currentModel.modelMatrix.rotate(EntityUtils.getEntityRotation(currentEntity)); -// currentModel.modelMatrix.scale(EntityUtils.getEntityScale(currentEntity)); -// currentModel.draw(); -// } - } - - if(renderHitboxes){ - for(Entity currentHitbox : Globals.hitboxManager.getAllHitboxes()){ - Model hitboxModel; - if(currentHitbox.getData(EntityDataStrings.COLLISION_ENTITY_DATA_TYPE).equals(EntityDataStrings.COLLISION_ENTITY_DATA_TYPE_HURT)){ - if((hitboxModel = Globals.assetManager.fetchModel("Models/unitsphere.fbx")) != null){ - Vector3f position = EntityUtils.getEntityPosition(currentHitbox); - modelTransformMatrix.identity(); - modelTransformMatrix.translate(new Vector3f(position).sub(CameraEntityUtils.getCameraCenter(Globals.playerCamera)).sub(new Vector3f(0,1,0))); - // modelTransformMatrix.translate(-0.25f, 0.0f, 0.5f); //center sphere - modelTransformMatrix.scale((Float)currentHitbox.getData(EntityDataStrings.COLLISION_ENTITY_DATA_RADIUS) * 2); - hitboxModel.modelMatrix = modelTransformMatrix; - hitboxModel.draw(); - } - } else if(currentHitbox.getData(EntityDataStrings.COLLISION_ENTITY_DATA_TYPE).equals(EntityDataStrings.COLLISION_ENTITY_DATA_TYPE_HIT)){ - if((hitboxModel = Globals.assetManager.fetchModel("Models/unitsphere_1.fbx")) != null){ - Vector3f position = EntityUtils.getEntityPosition(currentHitbox); - modelTransformMatrix.identity(); - modelTransformMatrix.translate(new Vector3f(position).sub(CameraEntityUtils.getCameraCenter(Globals.playerCamera)).sub(new Vector3f(0,1,0))); - // modelTransformMatrix.translate(-0.25f, 0.0f, 0.5f); //center sphere - modelTransformMatrix.scale((Float)currentHitbox.getData(EntityDataStrings.COLLISION_ENTITY_DATA_RADIUS) * 2); - hitboxModel.modelMatrix = modelTransformMatrix; - hitboxModel.draw(); - } - } - } - } - - - -// glBindVertexArray(0); - } - - static void renderScreenFramebuffer(){ - // - //unbind texture channels - // - //What does this mean? - //essentially there are two channels we're using to draw mesh textures - //we have to glBindTexture to pointer 0 for BOTH channels, otherwise - //the leftover texture gets used to draw the screen framebuffer quad - //which doesnt work - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, 0); - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, 0); - glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_2D, 0); - glActiveTexture(GL_TEXTURE3); - glBindTexture(GL_TEXTURE_2D, 0); - glActiveTexture(GL_TEXTURE0); - - - - //render full screen quad - glUseProgram(screenTextureShaders.shaderProgram); - glBindVertexArray(screenTextureVAO); - glBindTexture(GL_TEXTURE_2D, screenFramebuffer.getTexture()); -// glBindTexture(GL_TEXTURE_2D, lightDepthBuffer.getTexture()); - glDrawArrays(GL_TRIANGLES, 0, 6); - glBindVertexArray(0); - } - - - static void renderUI(){ - Matrix4f modelTransformMatrix = new Matrix4f(); - - for(Widget currentWidget : Globals.widgetManager.getWidgetList()){ - if(currentWidget.isDraw()){ - currentWidget.draw(); - } - } - -// for(Entity currentEntity : Globals.entityManager.getUIElements()){ -// Actor uiActor = EntityUtils.getEntityActor(currentEntity); -// if(currentEntity.getDataKeys().contains(EntityDataStrings.DATA_STRING_UI_ELEMENT_FONT)){ -// modelTransformMatrix.identity(); -// modelTransformMatrix.translate(EntityUtils.getEntityPosition(currentEntity)); -// uiActor.applyModelMatrix(modelTransformMatrix); -// } -// uiActor.drawUI(); -// } - } - - static void renderBlackBackground(){ - //render full screen quad - glUseProgram(screenTextureShaders.shaderProgram); - glBindVertexArray(screenTextureVAO); - Globals.blackTexture.bind(); -// glBindTexture(GL_TEXTURE_2D, screenFramebuffer.getTexture()); - glDrawArrays(GL_TRIANGLES, 0, 6); - glBindVertexArray(0); - } - } diff --git a/src/main/java/electrosphere/renderer/RenderingEngine.java b/src/main/java/electrosphere/renderer/RenderingEngine.java new file mode 100644 index 00000000..d768a3fd --- /dev/null +++ b/src/main/java/electrosphere/renderer/RenderingEngine.java @@ -0,0 +1,412 @@ +package electrosphere.renderer; + +import electrosphere.entity.CameraEntityUtils; +import electrosphere.entity.Entity; +import electrosphere.entity.EntityDataStrings; +import electrosphere.entity.EntityUtils; +import electrosphere.main.Globals; +import static electrosphere.main.Main.deltaTime; +import static electrosphere.main.Main.view_Range; +import static electrosphere.renderer.RenderUtils.createScreenTextureVAO; +import electrosphere.renderer.framebuffer.Framebuffer; +import electrosphere.renderer.framebuffer.FramebufferUtils; +import electrosphere.renderer.framebuffer.Renderbuffer; +import electrosphere.renderer.ui.Widget; +import org.joml.Matrix4f; +import org.joml.Vector3f; +import static org.lwjgl.glfw.GLFW.GLFW_CONTEXT_VERSION_MAJOR; +import static org.lwjgl.glfw.GLFW.GLFW_CONTEXT_VERSION_MINOR; +import static org.lwjgl.glfw.GLFW.GLFW_CURSOR; +import static org.lwjgl.glfw.GLFW.GLFW_CURSOR_DISABLED; +import static org.lwjgl.glfw.GLFW.GLFW_OPENGL_CORE_PROFILE; +import static org.lwjgl.glfw.GLFW.GLFW_OPENGL_PROFILE; +import static org.lwjgl.glfw.GLFW.glfwCreateWindow; +import static org.lwjgl.glfw.GLFW.glfwInit; +import static org.lwjgl.glfw.GLFW.glfwMakeContextCurrent; +import static org.lwjgl.glfw.GLFW.glfwMaximizeWindow; +import static org.lwjgl.glfw.GLFW.glfwPollEvents; +import static org.lwjgl.glfw.GLFW.glfwSetInputMode; +import static org.lwjgl.glfw.GLFW.glfwSwapBuffers; +import static org.lwjgl.glfw.GLFW.glfwTerminate; +import static org.lwjgl.glfw.GLFW.glfwWindowHint; +import org.lwjgl.opengl.GL; +import static org.lwjgl.opengl.GL11.GL_BLEND; +import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT; +import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT; +import static org.lwjgl.opengl.GL11.GL_DEPTH_TEST; +import static org.lwjgl.opengl.GL11.GL_ONE_MINUS_SRC_ALPHA; +import static org.lwjgl.opengl.GL11.GL_SRC_ALPHA; +import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D; +import static org.lwjgl.opengl.GL11.GL_TRIANGLES; +import static org.lwjgl.opengl.GL11.glBindTexture; +import static org.lwjgl.opengl.GL11.glBlendFunc; +import static org.lwjgl.opengl.GL11.glClear; +import static org.lwjgl.opengl.GL11.glClearColor; +import static org.lwjgl.opengl.GL11.glDisable; +import static org.lwjgl.opengl.GL11.glDrawArrays; +import static org.lwjgl.opengl.GL11.glEnable; +import static org.lwjgl.opengl.GL11.glViewport; +import static org.lwjgl.opengl.GL13.GL_TEXTURE0; +import static org.lwjgl.opengl.GL13.GL_TEXTURE1; +import static org.lwjgl.opengl.GL13.GL_TEXTURE2; +import static org.lwjgl.opengl.GL13.GL_TEXTURE3; +import static org.lwjgl.opengl.GL13.glActiveTexture; +import static org.lwjgl.opengl.GL20.glGetUniformLocation; +import static org.lwjgl.opengl.GL20.glUniformMatrix4fv; +import static org.lwjgl.opengl.GL20.glUseProgram; +import static org.lwjgl.opengl.GL30.GL_FRAMEBUFFER; +import static org.lwjgl.opengl.GL30.GL_RENDERBUFFER; +import static org.lwjgl.opengl.GL30.glBindFramebuffer; +import static org.lwjgl.opengl.GL30.glBindRenderbuffer; +import static org.lwjgl.opengl.GL30.glBindVertexArray; +import static org.lwjgl.system.MemoryUtil.NULL; + +public class RenderingEngine { + + + + public static final int GL_DEFAULT_FRAMEBUFFER = 0; + public static final int GL_DEFAULT_RENDERBUFFER = 0; + static Framebuffer screenFramebuffer; + static Renderbuffer screenRenderbuffer; + static int screenTextureVAO; + static ShaderProgram screenTextureShaders; + + static ShaderProgram lightDepthShaderProgram; + static Framebuffer lightDepthBuffer; + + public static boolean renderHitboxes = true; + + + + public void createOpenglContext(){ + //Sets the variables that control the window sizing + int screenWidth = Globals.WINDOW_WIDTH; + int screenHeight = Globals.WINDOW_HEIGHT; + //Initializes opengl + glfwInit(); + //Gives hints to glfw to control how opengl will be used + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); +// glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); Allows you to make the background transparent +// glfwWindowHint(GLFW_OPACITY, 23); + //Creates the window reference object + Globals.window = glfwCreateWindow(screenWidth, screenHeight, "LearnOpenGL", NULL, NULL); + //Errors for failure to create window (IE: No GUI mode on linux ?) + if (Globals.window == NULL) { + System.out.println("Failed to make window."); + glfwTerminate(); + } + //Makes the window that was just created the current OS-level window context + glfwMakeContextCurrent(Globals.window); + //Maximize it + glfwMaximizeWindow(Globals.window); + //Creates the OpenGL capabilities for the program. + GL.createCapabilities(); + + //This enables Z-buffering so that farther-back polygons are not drawn over nearer ones + glEnable(GL_DEPTH_TEST); + + // Support for transparency + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + //Hide the cursor and capture it + glfwSetInputMode(Globals.window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + + + + //init screen rendering quadrant + screenTextureVAO = createScreenTextureVAO(); +// initScreenTextureShaderProgram(); + screenTextureShaders = ShaderProgram.loadSpecificShader("/Shaders/screentexture/simple1/simple1.vs", "/Shaders/screentexture/simple1/simple1.fs"); +// screenTextureShaders = ShaderProgram.loadSpecificShader("/Shaders/screentexture/drawDepthBuffer/drawDepthBuffer.vs", "/Shaders/screentexture/drawDepthBuffer/drawDepthBuffer.fs"); + + //generate framebuffers + screenFramebuffer = FramebufferUtils.generateScreensizeTextureFramebuffer(); + glBindFramebuffer(GL_FRAMEBUFFER, GL_DEFAULT_FRAMEBUFFER); + glBindRenderbuffer(GL_RENDERBUFFER, GL_DEFAULT_RENDERBUFFER); + + lightDepthShaderProgram = ShaderProgram.loadSpecificShader("/Shaders/lightDepth/lightDepth.vs", "/Shaders/lightDepth/lightDepth.fs"); + Globals.depthMapShaderProgramLoc = lightDepthShaderProgram.shaderProgram; + lightDepthBuffer = FramebufferUtils.generateDepthBuffer(); + Globals.shadowMapTextureLoc = lightDepthBuffer.getTexture(); +// glEnable(GL_CULL_FACE); // enabled for shadow mapping + + + // + // Projection and View matrix creation + // + Globals.projectionMatrix = new Matrix4f(); + Globals.viewMatrix = new Matrix4f(); + float FOV = (float)(120.0f * Math.PI /180.0f); + Globals.projectionMatrix.setPerspective(FOV, 1.0f, 0.1f, view_Range); + Globals.viewMatrix.translation(new Vector3f(0.0f,0.0f,-3.0f)); + } + + + public void drawScreen(){ + + // + //first pass: generate depth map + // + if(Globals.RENDER_FLAG_RENDER_SHADOW_MAP){ + renderShadowMapFramebufferContent(); + } + + + /* + Render content to the game framebuffer + */ + if(Globals.RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER_CONTENT){ + renderGameFramebufferContent(); + } + + + + + //bind default FBO + glBindFramebuffer(GL_FRAMEBUFFER,0); + glDisable(GL_DEPTH_TEST); + + glClearColor(1.0f, 1.0f, 1.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + /* + Render the game framebuffer texture to a quad + */ + if(Globals.RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER){ + renderScreenFramebuffer(); + } + + /* + Render black background + */ + if(Globals.RENDER_FLAG_RENDER_BLACK_BACKGROUND){ + renderBlackBackground(); + } + + /* + Render any ui elements + */ + if(Globals.RENDER_FLAG_RENDER_UI){ + renderUI(); + } + + + + + + + //check and call events and swap the buffers + glfwSwapBuffers(Globals.window); + glfwPollEvents(); + } + + + static void renderShadowMapFramebufferContent(){ + + Matrix4f modelTransformMatrix = new Matrix4f(); + + //set the viewport to shadow map size + glViewport(0, 0, 4096, 4096); + glEnable(GL_DEPTH_TEST); + lightDepthBuffer.bind(); + glClear(GL_DEPTH_BUFFER_BIT); + + //set matrices for light render + float near_plane = 0.001f, far_plane = 100.5f; + Matrix4f lightProjection = new Matrix4f().setOrtho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane);//glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane); + Matrix4f lightView = new Matrix4f().lookAt( + new Vector3f(-20.0f, 40.0f, -10.0f), + new Vector3f( 0.0f, 0.0f, 0.0f), + new Vector3f( 0.0f, 1.0f, 0.0f) + ); + Globals.lightDepthMatrix = new Matrix4f(lightProjection).mul(lightView); + + glUseProgram(lightDepthShaderProgram.shaderProgram); + glUniformMatrix4fv(glGetUniformLocation(lightDepthShaderProgram.shaderProgram, "lightSpaceMatrix"), false, Globals.lightDepthMatrix.get(new float[16])); + +// glCullFace(GL_FRONT); + + // + // D R A W A L L E N T I T I E S + // + modelTransformMatrix = new Matrix4f(); + for(Entity currentEntity : Globals.entityManager.getDrawable()){ + //fetch actor + Actor currentActor = EntityUtils.getEntityActor(currentEntity); + //increment animations + if(currentActor.getCurrentAnimation() != null){ + currentActor.incrementAnimationTime(deltaTime * 500); + } + //calculate and apply model transform + modelTransformMatrix.identity(); + modelTransformMatrix.translate(new Vector3f(EntityUtils.getEntityPosition(currentEntity)).sub(CameraEntityUtils.getCameraCenter(Globals.playerCamera)).sub(new Vector3f(0,1,0))); + modelTransformMatrix.rotate(EntityUtils.getEntityRotation(currentEntity)); + modelTransformMatrix.scale(EntityUtils.getEntityScale(currentEntity)); + currentActor.applyModelMatrix(modelTransformMatrix); + //draw + currentActor.drawForDepthBuffer(); + } + + + + //bind default framebuffer + glBindFramebuffer(GL_FRAMEBUFFER,0); + //reset texture + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); + //reset the viewport to screen size + glViewport(0, 0, Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT); + //resume culling backface +// glCullFace(GL_BACK); + } + + + static void renderGameFramebufferContent(){ + + Matrix4f modelTransformMatrix = new Matrix4f(); + + //bind screen fbo + screenFramebuffer.bind(); + glEnable(GL_DEPTH_TEST); + + /// + /// R E N D E R I N G S T U F F + /// + //Sets the background color. + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + + // + // D R A W A L L E N T I T I E S + // + modelTransformMatrix = new Matrix4f(); + for(Entity currentEntity : Globals.entityManager.getDrawable()){ + //fetch actor + Actor currentActor = EntityUtils.getEntityActor(currentEntity); + //increment animations + //this is incremented in the shadow calculations +// if(currentActor.getCurrentAnimation() != null){ +// currentActor.incrementAnimationTime(deltaTime * 500); +// } + //calculate and apply model transform + modelTransformMatrix.identity(); + modelTransformMatrix.translate(new Vector3f(EntityUtils.getEntityPosition(currentEntity)).sub(CameraEntityUtils.getCameraCenter(Globals.playerCamera)).sub(new Vector3f(0,1,0))); + modelTransformMatrix.rotate(EntityUtils.getEntityRotation(currentEntity)); + modelTransformMatrix.scale(EntityUtils.getEntityScale(currentEntity)); + currentActor.applyModelMatrix(modelTransformMatrix); + //draw + currentActor.draw(); + +// Model currentModel = Globals.assetManager.fetchModel(EntityUtils.getEntityModelPath(currentEntity)); +// if(currentModel != null){ +// if(currentModel.currentAnimation != null){ +// currentModel.incrementTime(deltaTime * 500); +// } +// currentModel.modelMatrix = new Matrix4f(); +// currentModel.modelMatrix.translate(new Vector3f(EntityUtils.getEntityPosition(currentEntity)).sub(CameraEntityUtils.getCameraCenter(Globals.playerCamera)).sub(new Vector3f(0,1,0))); +// currentModel.modelMatrix.rotate(EntityUtils.getEntityRotation(currentEntity)); +// currentModel.modelMatrix.scale(EntityUtils.getEntityScale(currentEntity)); +// currentModel.draw(); +// } + } + + if(renderHitboxes){ + for(Entity currentHitbox : Globals.hitboxManager.getAllHitboxes()){ + Model hitboxModel; + if(currentHitbox.getData(EntityDataStrings.COLLISION_ENTITY_DATA_TYPE).equals(EntityDataStrings.COLLISION_ENTITY_DATA_TYPE_HURT)){ + if((hitboxModel = Globals.assetManager.fetchModel("Models/unitsphere.fbx")) != null){ + Vector3f position = EntityUtils.getEntityPosition(currentHitbox); + modelTransformMatrix.identity(); + modelTransformMatrix.translate(new Vector3f(position).sub(CameraEntityUtils.getCameraCenter(Globals.playerCamera)).sub(new Vector3f(0,1,0))); + // modelTransformMatrix.translate(-0.25f, 0.0f, 0.5f); //center sphere + modelTransformMatrix.scale((Float)currentHitbox.getData(EntityDataStrings.COLLISION_ENTITY_DATA_RADIUS) * 2); + hitboxModel.modelMatrix = modelTransformMatrix; + hitboxModel.draw(); + } + } else if(currentHitbox.getData(EntityDataStrings.COLLISION_ENTITY_DATA_TYPE).equals(EntityDataStrings.COLLISION_ENTITY_DATA_TYPE_HIT)){ + if((hitboxModel = Globals.assetManager.fetchModel("Models/unitsphere_1.fbx")) != null){ + Vector3f position = EntityUtils.getEntityPosition(currentHitbox); + modelTransformMatrix.identity(); + modelTransformMatrix.translate(new Vector3f(position).sub(CameraEntityUtils.getCameraCenter(Globals.playerCamera)).sub(new Vector3f(0,1,0))); + // modelTransformMatrix.translate(-0.25f, 0.0f, 0.5f); //center sphere + modelTransformMatrix.scale((Float)currentHitbox.getData(EntityDataStrings.COLLISION_ENTITY_DATA_RADIUS) * 2); + hitboxModel.modelMatrix = modelTransformMatrix; + hitboxModel.draw(); + } + } + } + } + + + +// glBindVertexArray(0); + } + + static void renderScreenFramebuffer(){ + // + //unbind texture channels + // + //What does this mean? + //essentially there are two channels we're using to draw mesh textures + //we have to glBindTexture to pointer 0 for BOTH channels, otherwise + //the leftover texture gets used to draw the screen framebuffer quad + //which doesnt work + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, 0); + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, 0); + glActiveTexture(GL_TEXTURE3); + glBindTexture(GL_TEXTURE_2D, 0); + glActiveTexture(GL_TEXTURE0); + + + + //render full screen quad + glUseProgram(screenTextureShaders.shaderProgram); + glBindVertexArray(screenTextureVAO); + glBindTexture(GL_TEXTURE_2D, screenFramebuffer.getTexture()); +// glBindTexture(GL_TEXTURE_2D, lightDepthBuffer.getTexture()); + glDrawArrays(GL_TRIANGLES, 0, 6); + glBindVertexArray(0); + } + + + static void renderUI(){ + Matrix4f modelTransformMatrix = new Matrix4f(); + + for(Widget currentWidget : Globals.widgetManager.getWidgetList()){ + if(currentWidget.isDraw()){ + currentWidget.draw(); + } + } + +// for(Entity currentEntity : Globals.entityManager.getUIElements()){ +// Actor uiActor = EntityUtils.getEntityActor(currentEntity); +// if(currentEntity.getDataKeys().contains(EntityDataStrings.DATA_STRING_UI_ELEMENT_FONT)){ +// modelTransformMatrix.identity(); +// modelTransformMatrix.translate(EntityUtils.getEntityPosition(currentEntity)); +// uiActor.applyModelMatrix(modelTransformMatrix); +// } +// uiActor.drawUI(); +// } + } + + static void renderBlackBackground(){ + //render full screen quad + glUseProgram(screenTextureShaders.shaderProgram); + glBindVertexArray(screenTextureVAO); + Globals.blackTexture.bind(); +// glBindTexture(GL_TEXTURE_2D, screenFramebuffer.getTexture()); + glDrawArrays(GL_TRIANGLES, 0, 6); + glBindVertexArray(0); + } + + +}