001package squidpony.squidgrid.gui.gdx; 002 003import com.badlogic.gdx.graphics.Color; 004import com.badlogic.gdx.math.MathUtils; 005import squidpony.squidgrid.mapping.WorldMapGenerator; 006import squidpony.squidmath.DiverRNG; 007import squidpony.squidmath.NumberTools; 008 009/** 010 * Created by Tommy Ettinger on 9/6/2019. 011 */ 012public class WorldMapView { 013 protected int width, height; 014 protected float[][] colorMap; 015 protected WorldMapGenerator world; 016 protected WorldMapGenerator.DetailedBiomeMapper biomeMapper; 017 018 public int getWidth() { 019 return width; 020 } 021 022 public int getHeight() { 023 return height; 024 } 025 026 public float[][] getColorMap() { 027 return colorMap; 028 } 029 030 public WorldMapGenerator.DetailedBiomeMapper getBiomeMapper() { 031 return biomeMapper; 032 } 033 034 public void setBiomeMapper(WorldMapGenerator.DetailedBiomeMapper biomeMapper) { 035 this.biomeMapper = biomeMapper; 036 } 037 038 public WorldMapGenerator getWorld() { 039 return world; 040 } 041 042 public void setWorld(WorldMapGenerator world) { 043 this.world = world; 044 if(this.width != world.width || this.height != world.height) 045 { 046 width = world.width; 047 height = world.height; 048 colorMap = new float[width][height]; 049 } 050 } 051 052 public WorldMapView(WorldMapGenerator worldMapGenerator) 053 { 054 world = worldMapGenerator == null ? new WorldMapGenerator.LocalMap() : worldMapGenerator; 055 width = world.width; 056 height = world.height; 057 colorMap = new float[width][height]; 058 this.biomeMapper = new WorldMapGenerator.DetailedBiomeMapper(); 059 initialize(); 060 } 061 062 public WorldMapView(long seed, int width, int height) 063 { 064 this(new WorldMapGenerator.LocalMap(seed, width, height)); 065 } 066 067 public static final int 068 Desert = 0 , 069 Savanna = 1 , 070 TropicalRainforest = 2 , 071 Grassland = 3 , 072 Woodland = 4 , 073 SeasonalForest = 5 , 074 TemperateRainforest = 6 , 075 BorealForest = 7 , 076 Tundra = 8 , 077 Ice = 9 , 078 Beach = 10, 079 Rocky = 11, 080 Shallow = 12, 081 Ocean = 13, 082 Empty = 14; 083 084 public static float iceColor = SColor.floatGetI(240, 248, 255); 085 public static float desertColor = SColor.floatGetI(248, 229, 180); 086 public static float savannaColor = SColor.floatGetI(181, 200, 100); 087 public static float tropicalRainforestColor = SColor.floatGetI(66, 123, 25); 088 public static float tundraColor = SColor.floatGetI(151, 175, 159); 089 public static float temperateRainforestColor = SColor.floatGetI(54, 113, 60); 090 public static float grasslandColor = SColor.floatGetI(169, 185, 105); 091 public static float seasonalForestColor = SColor.floatGetI(100, 158, 75); 092 public static float borealForestColor = SColor.floatGetI(75, 105, 45); 093 public static float woodlandColor = SColor.floatGetI(122, 170, 90); 094 public static float rockyColor = SColor.floatGetI(171, 175, 145); 095 public static float beachColor = SColor.floatGetI(255, 235, 180); 096 public static float emptyColor = SColor.floatGetI(34, 32, 52); 097 098 // water colors 099 public static float deepColor = SColor.floatGetI(0, 42, 88); 100 public static float shallowColor = SColor.floatGetI(20, 145, 197); 101 public static float foamColor = SColor.floatGetI(61, 162, 215); 102 103 protected float[] biomeColors = { 104 desertColor, 105 savannaColor, 106 tropicalRainforestColor, 107 grasslandColor, 108 woodlandColor, 109 seasonalForestColor, 110 temperateRainforestColor, 111 borealForestColor, 112 tundraColor, 113 iceColor, 114 beachColor, 115 rockyColor, 116 shallowColor, 117 deepColor, 118 emptyColor 119 }; 120 121 public final static float[] BIOME_TABLE = { 122 //COLDEST //COLDER //COLD //HOT //HOTTER //HOTTEST 123 Ice+0.85f, Ice+0.65f, Grassland+0.9f, Desert+0.75f, Desert+0.8f, Desert+0.85f, //DRYEST 124 Ice+0.7f, Tundra+0.9f, Grassland+0.6f, Grassland+0.3f, Desert+0.65f, Desert+0.7f, //DRYER 125 Ice+0.55f, Tundra+0.7f, Woodland+0.4f, Woodland+0.6f, Savanna+0.8f, Desert+0.6f, //DRY 126 Ice+0.4f, Tundra+0.5f, SeasonalForest+0.3f, SeasonalForest+0.5f, Savanna+0.6f, Savanna+0.4f, //WET 127 Ice+0.2f, Tundra+0.3f, BorealForest+0.35f, TemperateRainforest+0.4f, TropicalRainforest+0.6f, Savanna+0.2f, //WETTER 128 Ice+0.0f, BorealForest, BorealForest+0.15f, TemperateRainforest+0.2f, TropicalRainforest+0.4f, TropicalRainforest+0.2f, //WETTEST 129 Rocky+0.9f, Rocky+0.6f, Beach+0.4f, Beach+0.55f, Beach+0.75f, Beach+0.9f, //COASTS 130 Ice+0.3f, Shallow+0.9f, Shallow+0.75f, Shallow+0.6f, Shallow+0.5f, Shallow+0.4f, //RIVERS 131 Ice+0.2f, Shallow+0.9f, Shallow+0.65f, Shallow+0.5f, Shallow+0.4f, Shallow+0.3f, //LAKES 132 Ocean+0.9f, Ocean+0.75f, Ocean+0.6f, Ocean+0.45f, Ocean+0.3f, Ocean+0.15f, //OCEANS 133 Empty //SPACE 134 }; 135 public final float[] BIOME_COLOR_TABLE = new float[61], BIOME_DARK_COLOR_TABLE = new float[61]; 136 137 public void initialize() 138 { 139 initialize(0f, 0f, 0f, 1f); 140 } 141 142 public void initialize(float hue, float saturation, float brightness, float contrast) 143 { 144 float b, diff; 145 for (int i = 0; i < 60; i++) { 146 b = BIOME_TABLE[i]; 147 diff = ((b % 1.0f) - 0.48f) * 0.27f * contrast; 148 BIOME_COLOR_TABLE[i] = b = SColor.toEditedFloat((diff >= 0) 149 ? SColor.lightenFloat(biomeColors[(int)b], diff) 150 : SColor.darkenFloat(biomeColors[(int)b], -diff), hue, saturation, brightness, 0f); 151 BIOME_DARK_COLOR_TABLE[i] = SColor.darkenFloat(b, 0.08f); 152 } 153 BIOME_COLOR_TABLE[60] = BIOME_DARK_COLOR_TABLE[60] = emptyColor; 154 } 155 156 /** 157 * Initializes the colors to use for each biome (these are almost always mixed with other biome colors in practice). 158 * Each parameter may be null to use the default for an Earth-like world; otherwise it should be a libGDX 159 * {@link Color} or some subclass, like {@link SColor}. All non-null parameters should probably be fully opaque, 160 * except {@code emptyColor}, which is only used for world maps that show empty space (like a globe, as produced by 161 * {@link WorldMapGenerator.RotatingSpaceMap}). 162 * @param desertColor hot, dry, barren land; may be sandy, but many real-world deserts don't have much sand 163 * @param savannaColor hot, mostly-dry land with some parched vegetation; also called scrub or chaparral 164 * @param tropicalRainforestColor hot, extremely wet forests with dense rich vegetation 165 * @param grasslandColor prairies that are dry and usually wind-swept, but not especially hot or cold 166 * @param woodlandColor part-way between a prairie and a forest; not especially hot or cold 167 * @param seasonalForestColor forest that becomes barren in winter (deciduous trees); not especially hot or cold 168 * @param temperateRainforestColor forest that tends to be slightly warm but very wet 169 * @param borealForestColor forest that tends to be cold and very wet 170 * @param tundraColor very cold plains that still have some low-lying vegetation; also called taiga 171 * @param iceColor cold barren land covered in permafrost; also used for rivers and lakes that are frozen 172 * @param beachColor sandy or otherwise light-colored shorelines; here, these are more common in warmer places 173 * @param rockyColor rocky or otherwise rugged shorelines; here, these are more common in colder places 174 * @param shallowColor the color of very shallow water; will be mixed with {@code deepColor} to get most ocean colors 175 * @param deepColor the color of very deep water; will be mixed with {@code shallowColor} to get most ocean colors 176 * @param emptyColor the color used for empty space off the edge of the world map; may be transparent 177 */ 178 public void initialize( 179 Color desertColor, 180 Color savannaColor, 181 Color tropicalRainforestColor, 182 Color grasslandColor, 183 Color woodlandColor, 184 Color seasonalForestColor, 185 Color temperateRainforestColor, 186 Color borealForestColor, 187 Color tundraColor, 188 Color iceColor, 189 Color beachColor, 190 Color rockyColor, 191 Color shallowColor, 192 Color deepColor, 193 Color emptyColor 194 ) 195 { 196 biomeColors[ 0] = desertColor == null ? WorldMapView.desertColor : desertColor.toFloatBits(); 197 biomeColors[ 1] = savannaColor == null ? WorldMapView.savannaColor : savannaColor.toFloatBits(); 198 biomeColors[ 2] = tropicalRainforestColor == null ? WorldMapView.tropicalRainforestColor : tropicalRainforestColor.toFloatBits(); 199 biomeColors[ 3] = grasslandColor == null ? WorldMapView.grasslandColor : grasslandColor.toFloatBits(); 200 biomeColors[ 4] = woodlandColor == null ? WorldMapView.woodlandColor : woodlandColor.toFloatBits(); 201 biomeColors[ 5] = seasonalForestColor == null ? WorldMapView.seasonalForestColor : seasonalForestColor.toFloatBits(); 202 biomeColors[ 6] = temperateRainforestColor == null ? WorldMapView.temperateRainforestColor : temperateRainforestColor.toFloatBits(); 203 biomeColors[ 7] = borealForestColor == null ? WorldMapView.borealForestColor : borealForestColor.toFloatBits(); 204 biomeColors[ 8] = tundraColor == null ? WorldMapView.tundraColor : tundraColor.toFloatBits(); 205 biomeColors[ 9] = iceColor == null ? WorldMapView.iceColor : iceColor.toFloatBits(); 206 biomeColors[10] = beachColor == null ? WorldMapView.beachColor : beachColor.toFloatBits(); 207 biomeColors[11] = rockyColor == null ? WorldMapView.rockyColor : rockyColor.toFloatBits(); 208 biomeColors[12] = shallowColor == null ? WorldMapView.shallowColor : shallowColor.toFloatBits(); 209 biomeColors[13] = deepColor == null ? WorldMapView.deepColor : deepColor.toFloatBits(); 210 biomeColors[14] = emptyColor == null ? WorldMapView.emptyColor : emptyColor.toFloatBits(); 211 float b, diff; 212 for (int i = 0; i < 60; i++) { 213 b = BIOME_TABLE[i]; 214 diff = ((b % 1.0f) - 0.48f) * 0.27f; 215 BIOME_COLOR_TABLE[i] = b = (diff >= 0) 216 ? SColor.lightenFloat(biomeColors[(int)b], diff) 217 : SColor.darkenFloat(biomeColors[(int)b], -diff); 218 BIOME_DARK_COLOR_TABLE[i] = SColor.darkenFloat(b, 0.08f); 219 } 220 BIOME_COLOR_TABLE[60] = BIOME_DARK_COLOR_TABLE[60] = biomeColors[14]; 221 biomeColors[ 0] = WorldMapView.desertColor; 222 biomeColors[ 1] = WorldMapView.savannaColor; 223 biomeColors[ 2] = WorldMapView.tropicalRainforestColor; 224 biomeColors[ 3] = WorldMapView.grasslandColor; 225 biomeColors[ 4] = WorldMapView.woodlandColor; 226 biomeColors[ 5] = WorldMapView.seasonalForestColor; 227 biomeColors[ 6] = WorldMapView.temperateRainforestColor; 228 biomeColors[ 7] = WorldMapView.borealForestColor; 229 biomeColors[ 8] = WorldMapView.tundraColor; 230 biomeColors[ 9] = WorldMapView.iceColor; 231 biomeColors[10] = WorldMapView.beachColor; 232 biomeColors[11] = WorldMapView.rockyColor; 233 biomeColors[12] = WorldMapView.shallowColor; 234 biomeColors[13] = WorldMapView.deepColor; 235 biomeColors[14] = WorldMapView.emptyColor; 236 } 237 238 /** 239 * Initializes the colors to use in some combination for all biomes, without regard for what the biome really is. 240 * There should be at least one packed float color given in similarColors, but there can be many of them. 241 * @param similarColors an array or vararg of packed float colors with at least one element 242 */ 243 public void match( 244 float... similarColors 245 ) 246 { 247 for (int i = 0; i < 14; i++) { 248 biomeColors[i] = SColor.lerpFloatColors(similarColors[i % similarColors.length], similarColors[(i * 5 + 3) % similarColors.length], 0.5f); 249 } 250 biomeColors[14] = WorldMapView.emptyColor; 251 float b, diff; 252 for (int i = 0; i < 60; i++) { 253 b = BIOME_TABLE[i]; 254 diff = ((b % 1.0f) - 0.48f) * 0.27f; 255 BIOME_COLOR_TABLE[i] = b = (diff >= 0) 256 ? SColor.lightenFloat(biomeColors[(int)b], diff) 257 : SColor.darkenFloat(biomeColors[(int)b], -diff); 258 BIOME_DARK_COLOR_TABLE[i] = SColor.darkenFloat(b, 0.08f); 259 } 260 BIOME_COLOR_TABLE[60] = BIOME_DARK_COLOR_TABLE[60] = biomeColors[14]; 261 biomeColors[ 0] = WorldMapView.desertColor; 262 biomeColors[ 1] = WorldMapView.savannaColor; 263 biomeColors[ 2] = WorldMapView.tropicalRainforestColor; 264 biomeColors[ 3] = WorldMapView.grasslandColor; 265 biomeColors[ 4] = WorldMapView.woodlandColor; 266 biomeColors[ 5] = WorldMapView.seasonalForestColor; 267 biomeColors[ 6] = WorldMapView.temperateRainforestColor; 268 biomeColors[ 7] = WorldMapView.borealForestColor; 269 biomeColors[ 8] = WorldMapView.tundraColor; 270 biomeColors[ 9] = WorldMapView.iceColor; 271 biomeColors[10] = WorldMapView.beachColor; 272 biomeColors[11] = WorldMapView.rockyColor; 273 biomeColors[12] = WorldMapView.shallowColor; 274 biomeColors[13] = WorldMapView.deepColor; 275 biomeColors[14] = WorldMapView.emptyColor; 276 } 277 278 public void generate() 279 { 280// generate(world.seedA, world.seedB, 0.9 + NumberTools.formCurvedDouble((world.seedA ^ 0x123456789ABCDL) * 0x12345689ABL ^ world.seedB) * 0.15, 281// DiverRNG.determineDouble(world.seedB * 0x12345L + 0x54321L ^ world.seedA) * 0.2 + 1.0); 282 generate(world.seedA, world.seedB, 1.0 + NumberTools.formCurvedDouble((world.seedA ^ 0x123456789ABCDL) * 0x12345689ABL ^ world.seedB) * 0.25, 283 DiverRNG.determineDouble(world.seedB * 0x12345L + 0x54321L ^ world.seedA) * 0.25 + 1.0); 284 } 285 public void generate(double landMod, double heatMod) 286 { 287 generate(world.seedA, world.seedB, landMod, heatMod); 288 } 289 290 public void generate(int seedA, int seedB, double landMod, double heatMod) { 291 long seed = (long) seedB << 32 | (seedA & 0xFFFFFFFFL); 292 world.generate(landMod, heatMod, seed); 293 biomeMapper.makeBiomes(world); 294 } 295 public float[][] show() 296 { 297 int hc, tc, bc; 298 final int[][] heightCodeData = world.heightCodeData; 299 double[][] heightData = world.heightData; 300 int[][] heatCodeData = biomeMapper.heatCodeData; 301 int[][] biomeCodeData = biomeMapper.biomeCodeData; 302 303 for (int y = 0; y < height; y++) { 304 PER_CELL: 305 for (int x = 0; x < width; x++) { 306 hc = heightCodeData[x][y]; 307 if(hc == 1000) 308 { 309 colorMap[x][y] = emptyColor; 310 continue; 311 } 312 tc = heatCodeData[x][y]; 313 bc = biomeCodeData[x][y]; 314 if(tc == 0) 315 { 316 switch (hc) 317 { 318 case 0: 319 case 1: 320 case 2: 321 case 3: 322 colorMap[x][y] = SColor.lerpFloatColors(BIOME_COLOR_TABLE[50], BIOME_COLOR_TABLE[12], 323 (float) ((heightData[x][y] - -1.0) / (WorldMapGenerator.sandLower - -1.0))); 324 continue PER_CELL; 325 case 4: 326 colorMap[x][y] = SColor.lerpFloatColors(BIOME_COLOR_TABLE[0], BIOME_COLOR_TABLE[12], 327 (float) ((heightData[x][y] - WorldMapGenerator.sandLower) / (WorldMapGenerator.sandUpper - WorldMapGenerator.sandLower))); 328 continue PER_CELL; 329 } 330 } 331 switch (hc) { 332 case 0: 333 case 1: 334 case 2: 335 case 3: 336 colorMap[x][y] = SColor.lerpFloatColors( 337 BIOME_COLOR_TABLE[56], BIOME_COLOR_TABLE[43], 338 (MathUtils.clamp((float) (((heightData[x][y] + 0.06) * 8.0) / (WorldMapGenerator.sandLower + 1.0)), 0f, 1f))); 339 break; 340 default: 341 colorMap[x][y] = SColor.lerpFloatColors(BIOME_COLOR_TABLE[biomeMapper.extractPartB(bc)], 342 BIOME_DARK_COLOR_TABLE[biomeMapper.extractPartA(bc)], biomeMapper.extractMixAmount(bc)); 343 } 344 } 345 } 346 347 return colorMap; 348 } 349}