001package com.fs.starfarer.api.impl.campaign.terrain; 002 003import java.util.Arrays; 004import java.util.Random; 005import java.util.zip.DataFormatException; 006import java.util.zip.Deflater; 007import java.util.zip.Inflater; 008 009import java.awt.Color; 010 011import javax.xml.bind.DatatypeConverter; 012 013import org.lwjgl.opengl.GL11; 014import org.lwjgl.util.vector.Vector2f; 015 016import com.fs.starfarer.api.Global; 017import com.fs.starfarer.api.campaign.CampaignEngineLayers; 018import com.fs.starfarer.api.campaign.CampaignFleetAPI; 019import com.fs.starfarer.api.campaign.SectorEntityToken; 020import com.fs.starfarer.api.combat.ViewportAPI; 021import com.fs.starfarer.api.graphics.SpriteAPI; 022import com.fs.starfarer.api.util.Misc; 023 024public abstract class BaseTiledTerrain extends BaseTerrain { 025 026 public static class TileParams { 027 public String tiles; 028 public int w; 029 public int h; 030 public String cat; 031 public String key; 032 public int tW; 033 public int tH; 034 public String name; 035 public TileParams(String tiles, int width, int height, 036 String tileTexCat, String tileTexKey, int tilesWide, int tilesHigh, String name) { 037 this.tiles = tiles; 038 this.w = width; 039 this.h = height; 040 this.cat = tileTexCat; 041 this.key = tileTexKey; 042 this.tW = tilesWide; 043 this.tH = tilesHigh; 044 this.name = name; 045 } 046 047 } 048 049// public static class Tile { 050// public int cellX; 051// public int cellY; 052// public int texCellX; 053// public int texCellY; 054// } 055 056 protected TileParams params; 057 protected transient SpriteAPI texture; 058 protected transient SpriteAPI mapTexture; 059 //protected Tile[][] tiles; 060 protected transient int [][] tiles; 061 062 protected long tileSeed; 063 protected String savedTiles; 064 public void init(String terrainId, SectorEntityToken entity, Object param) { 065 super.init(terrainId, entity, param); 066 067 this.params = (TileParams) param; 068 name = params.name; 069 if (name == null) name = "Unknown"; 070 071 tiles = new int [params.w][params.h]; 072 073 074 tileSeed = new Random().nextLong(); 075// Random random = new Random(tileSeed); 076 077 for (int i = 0; i < tiles.length; i++) { 078 for (int j = 0; j < tiles[0].length; j++) { 079 int index = i + (tiles[0].length - j - 1) * tiles.length; 080 char c = params.tiles.charAt(index); 081 if (!Character.isWhitespace(c)) { 082// int texX = (int) (Math.random() * params.tW); 083// int texY = (int) (Math.random() * params.tH); 084// int texX = (int) (random.nextFloat() * params.tW); 085// int texY = (int) (random.nextFloat() * params.tH); 086// tiles[i][j] = texX + texY * params.tW; 087 tiles[i][j] = 1; 088 } else { 089 tiles[i][j] = -1; 090 } 091 } 092 } 093 094 savedTiles = encodeTiles(tiles); 095 readResolve(); 096 097 params.tiles = null; // don't need to save this 098 } 099 100 protected void regenTiles() { 101 Random random = new Random(tileSeed); 102 for (int i = 0; i < tiles.length; i++) { 103 for (int j = 0; j < tiles[0].length; j++) { 104 if (tiles[i][j] >= 0) { 105 int texX = (int) (random.nextFloat() * params.tW); 106 int texY = (int) (random.nextFloat() * params.tH); 107 tiles[i][j] = texX + texY * params.tW; 108 } else { 109 tiles[i][j] = -1; 110 } 111 } 112 } 113 sampleCache = null; 114 } 115 116 117 public int[][] getTiles() { 118 return tiles; 119 } 120 121 public TileParams getParams() { 122 return params; 123 } 124 125 126 Object readResolve() { 127 texture = Global.getSettings().getSprite(params.cat, params.key); 128 mapTexture = Global.getSettings().getSprite(params.cat, params.key + "_map"); 129 130 if (savedTiles != null) { 131 try { 132 tiles = decodeTiles(savedTiles, params.w, params.h); 133 } catch (DataFormatException e) { 134 throw new RuntimeException("Error decoding tiled terrain tiles", e); 135 } 136 } else { 137 // shouldn't be here, if we are then savedTiles == null and something went badly wrong 138 tiles = new int [params.w][params.h]; 139 } 140 regenTiles(); 141 142 return this; 143 } 144 145 Object writeReplace() { 146 params.tiles = null; 147 savedTiles = encodeTiles(tiles); 148 return this; 149 } 150 151 @Override 152 public boolean containsEntity(SectorEntityToken other) { 153 if (other.getContainingLocation() != this.entity.getContainingLocation()) return false; 154 return containsPoint(other.getLocation(), other.getRadius()) && !isPreventedFromAffecting(other); 155 } 156 157 public boolean containsPoint(Vector2f test, float r) { 158 159 float dist = Misc.getDistance(this.entity.getLocation(), test) - r; 160 if (dist > getRenderRange()) return false; 161 162 float x = this.entity.getLocation().x; 163 float y = this.entity.getLocation().y; 164 float size = getTileSize(); 165 float containsSize = getTileContainsSize(); 166 167 float w = tiles.length * size; 168 float h = tiles[0].length * size; 169 170 x -= w/2f; 171 y -= h/2f; 172 173 float extra = (containsSize - size) / 2f; 174 175 if (test.x + r + extra < x) return false; 176 if (test.y + r + extra < y) return false; 177 if (test.x > x + w + r + extra) return false; 178 if (test.y > y + h + r + extra) return false; 179 180 int xIndex = (int) ((test.x - x) / size); 181 int yIndex = (int) ((test.y - y) / size); 182 183 if (xIndex < 0) xIndex = 0; 184 if (yIndex < 0) yIndex = 0; 185 186 if (xIndex >= tiles.length) xIndex = tiles.length - 1; 187 if (yIndex >= tiles[0].length) yIndex = tiles[0].length - 1; 188 189// if (entity.isPlayerFleet()) { 190// System.out.println(this + " " + xIndex + "," + yIndex); 191// } 192 193 for (float i = Math.max(0, xIndex - 1); i <= xIndex + 1 && i < tiles.length; i++) { 194 for (float j = Math.max(0, yIndex - 1); j <= yIndex + 1 && j < tiles[0].length; j++) { 195 int texIndex = tiles[(int) i][(int) j]; 196 if (texIndex >= 0) { 197 float tx = x + i * size + size/2f - containsSize/2f; 198 float ty = y + j * size + size/2f - containsSize/2f; 199 200 if (test.x + r < tx) continue; 201 if (test.y + r < ty) continue; 202 if (test.x > tx + containsSize + r) continue; 203 if (test.y > ty + containsSize + r) continue; 204 return true; 205 } 206 } 207 } 208 return false; 209 } 210 211 212 public abstract float getTileSize(); 213 public abstract float getTileRenderSize(); 214 public abstract float getTileContainsSize(); 215 public abstract void preRender(CampaignEngineLayers layer, float alphaMult); 216 public abstract void preMapRender(float alphaMult); 217 public abstract Color getRenderColor(); 218 219 public float getRenderRange() { 220 float size = getTileSize(); 221 float renderSize = getTileRenderSize(); 222 float w = tiles.length * size * 0.5f + (renderSize - size) * 0.5f; 223 float h = tiles[0].length * size * 0.5f + (renderSize - size) * 0.5f; 224 return Math.max(w, h) * 1.5f; 225 } 226 227 public void render(CampaignEngineLayers layer, ViewportAPI v) { 228 texture.bindTexture(); 229 GL11.glEnable(GL11.GL_TEXTURE_2D); 230 //GL11.glDisable(GL11.GL_TEXTURE_2D); 231 232 preRender(layer, v.getAlphaMult()); 233 //GL11.glDisable(GL11.GL_TEXTURE_2D); 234 235 float x = this.entity.getLocation().x; 236 float y = this.entity.getLocation().y; 237 float size = getTileSize(); 238 float renderSize = getTileRenderSize(); 239 240 float w = tiles.length * size; 241 float h = tiles[0].length * size; 242 x -= w/2f; 243 y -= h/2f; 244 float extra = (renderSize - size) / 2f + 100f; 245 246 float llx = v.getLLX(); 247 float lly = v.getLLY(); 248 float vw = v.getVisibleWidth(); 249 float vh = v.getVisibleHeight(); 250 251 if (llx > x + w + extra) return; 252 if (lly > y + h + extra) return; 253 if (llx + vw + extra < x) return; 254 if (lly + vh + extra < y) return; 255 256 float xStart = (int)((llx - x - extra) / size); 257 if (xStart < 0) xStart = 0; 258 float yStart = (int)((lly - y - extra) / size); 259 if (yStart < 0) yStart = 0; 260 261 float xEnd = (int)((llx + vw - x + extra) / size) + 1; 262 if (xEnd >= tiles.length) xEnd = tiles.length - 1; 263 float yEnd = (int)((lly + vw - y + extra) / size) + 1; 264 if (yEnd >= tiles.length) yEnd = tiles[0].length - 1; 265 266 renderSubArea(xStart, xEnd, yStart, yEnd, 1f, 1, v.getAlphaMult()); 267 268 //renderSubArea(0, tiles.length, 0, tiles[0].length, 1f); 269 } 270 271 public boolean isTileVisible(int i, int j) { 272 float x = this.entity.getLocation().x; 273 float y = this.entity.getLocation().y; 274 float size = getTileSize(); 275 float renderSize = getTileRenderSize(); 276 277 float w = tiles.length * size; 278 float h = tiles[0].length * size; 279 x -= w/2f; 280 y -= h/2f; 281 float extra = (renderSize - size) / 2f + 100f; 282 283 ViewportAPI v = Global.getSector().getViewport(); 284 float llx = v.getLLX(); 285 float lly = v.getLLY(); 286 float vw = v.getVisibleWidth(); 287 float vh = v.getVisibleHeight(); 288 289 if (llx > x + w + extra) return false; 290 if (lly > y + h + extra) return false; 291 if (llx + vw + extra < x) return false; 292 if (lly + vh + extra < y) return false; 293 294 float xStart = (int)((llx - x - extra) / size); 295 if (xStart < 0) xStart = 0; 296 float yStart = (int)((lly - y - extra) / size); 297 if (yStart < 0) yStart = 0; 298 299 float xEnd = (int)((llx + vw - x + extra) / size) + 1; 300 if (xEnd >= tiles.length) xEnd = tiles.length - 1; 301 float yEnd = (int)((lly + vw - y + extra) / size) + 1; 302 if (yEnd >= tiles.length) yEnd = tiles[0].length - 1; 303 304 if (i < xStart) return false; 305 if (i > xEnd) return false; 306 if (j < yStart) return false; 307 if (j > yEnd) return false; 308 309 return true; 310 } 311 312 public void renderOnMap(float factor, float alphaMult) { 313 mapTexture.bindTexture(); 314 GL11.glEnable(GL11.GL_TEXTURE_2D); 315 preMapRender(alphaMult); 316 renderSubArea(0, tiles.length, 0, tiles[0].length, factor, getNumMapSamples(), alphaMult); 317 } 318 319 public int getNumMapSamples() { 320 return 5; 321 } 322 323 public void renderOnMapAbove(float factor, float alphaMult) { 324 325 } 326 327 public float[] getTileCenter(int i, int j) { 328 float x = entity.getLocation().x; 329 float y = entity.getLocation().y; 330 float size = getTileSize(); 331 332 float w = tiles.length * size; 333 float h = tiles[0].length * size; 334 335 float [] result = new float[2]; 336 result[0] = x - w / 2f + (float)i * size + size / 2f; 337 result[1] = y - h / 2f + (float)j * size + size / 2f; 338 return result; 339 } 340 341 public static class TileSample { 342 public int texIndex = -1; 343 public float angle = 0; 344 public float xOff = 0; 345 public float yOff = 0; 346 public float weight = 0; 347 } 348 349 protected transient TileSample [][] sampleCache = null; 350 protected transient int samplesForCache = 0; 351 352 public boolean isUseSampleCache() { 353 return false; 354 } 355 356 public void forceClearSampleCache() { 357 sampleCache = null; 358 } 359 public void updateSampleCache(int samples, boolean force) { 360 if (tiles == null) return; 361 if (sampleCache != null && samplesForCache == samples && !force) { 362 return; 363 } 364 samplesForCache = samples; 365 366 //(0, tiles.length, 0, tiles[0].length 367 368 int cacheW = tiles.length / samples + 1; 369 int cacheH = tiles[0].length / samples + 1; 370 sampleCache = new TileSample[cacheW][cacheH]; 371 372 float renderSize = getTileRenderSize(); 373 renderSize *= samples; 374 375 for (float i = 0; i <= tiles.length; i+=samples) { 376 if (i < 0 || i >= tiles.length) continue; 377 for (float j = 0; j <= tiles[0].length; j+=samples) { 378 if (j < 0 || j >= tiles[0].length) continue; 379 int texIndex = -1; 380 float angle = 0; 381 float xOff = 0; 382 float yOff = 0; 383 float weight = 0; 384 for (int m = 0; m < samples && i + m <= tiles.length; m++) { 385 if (i + m < 0 || i + m >= tiles.length) continue; 386 for (int n = 0; n < samples && j + n < tiles[0].length; n++) { 387 if (j + n < 0 || j + n >= tiles[0].length) continue; 388 int currIndex = tiles[(int) i + m][(int) j + n]; 389 if (currIndex >= 0 && texIndex < 0) { 390 texIndex = currIndex; 391 Random rand = new Random((long) (i + j * tiles.length) * 1000000); 392 angle = rand.nextFloat() * 360f; 393 float offRange = renderSize * 0.25f; 394 xOff = -offRange / 2f + offRange * rand.nextFloat(); 395 yOff = -offRange / 2f + offRange * rand.nextFloat(); 396// if (Keyboard.isKeyDown(Keyboard.KEY_O)) { 397// xOff = yOff = 0f; 398// } 399 } 400 if (currIndex >= 0) { 401 weight++; 402 } 403 } 404 } 405 TileSample sample = new TileSample(); 406 sample.texIndex = texIndex; 407 sample.angle = angle; 408 sample.xOff = xOff; 409 sample.yOff = yOff; 410 sample.weight = weight; 411 412 sampleCache[(int)i/samples][(int)j/samples] = sample; 413 } 414 } 415 } 416 417 418 419 protected void renderSubArea(float startColumn, float endColumn, float startRow, float endRow, float factor, int samples, float alphaMult) { 420 float x = entity.getLocation().x; 421 float y = entity.getLocation().y; 422 float size = getTileSize(); 423 float renderSize = getTileRenderSize(); 424 425 float w = tiles.length * size; 426 float h = tiles[0].length * size; 427 //if (true) return; 428 429 //Random rand = new Random(tiles.length + tiles[0].length); 430 //Random rand = new Random(); 431 432 if (samples == 1) { 433 GL11.glBegin(GL11.GL_QUADS); 434 for (float i = startColumn; i <= endColumn; i++) { 435 if (i < 0 || i >= tiles.length) continue; 436 for (float j = startRow; j <= endRow; j++) { 437 if (j < 0 || j >= tiles[0].length) continue; 438 int texIndex = tiles[(int) i][(int) j]; 439 if (texIndex >= 0) { 440 int texCellX = texIndex % params.tW; 441 int texCellY = texIndex / params.tW; 442 443 Random rand = new Random((long) (i + j * tiles.length) * 1000000); 444 float angle = rand.nextFloat() * 360f; 445 float offRange = renderSize * 0.25f; 446 float xOff = -offRange / 2f + offRange * rand.nextFloat(); 447 float yOff = -offRange / 2f + offRange * rand.nextFloat(); 448// if (Keyboard.isKeyDown(Keyboard.KEY_O)) { 449// xOff = yOff = 0f; 450// } 451 // angle += angleOffset; 452 // angle = Misc.normalizeAngle(angle); 453 renderQuad((int)i, (int)j, 454 (x + xOff - w / 2f + i * size + size/2f - renderSize/2f) * factor, 455 (y + yOff - h / 2f + j * size + size/2f - renderSize/2f) * factor, 456 renderSize * factor, renderSize * factor, 457 texCellX * 0.25f, texCellY * 0.25f, 458 0.25f, 0.25f, 459 angle); 460 } 461 } 462 } 463 GL11.glEnd(); 464 } else { 465 //renderSize = (size * samples) + (renderSize - size); 466 renderSize *= samples; 467 size *= samples; 468 alphaMult *= 0.67f; 469 //alphaMult = 1f; 470 GL11.glBegin(GL11.GL_QUADS); 471 float max = samples * samples; 472 for (float i = startColumn; i <= endColumn; i+=samples) { 473 if (i < 0 || i >= tiles.length) continue; 474 for (float j = startRow; j <= endRow; j+=samples) { 475 int texIndex = -1; 476 float angle = 0; 477 float xOff = 0; 478 float yOff = 0; 479 float weight = 0; 480 481 boolean usingSample = false; 482 if (isUseSampleCache()) { 483 updateSampleCache(samples, false); 484 if (sampleCache != null) { 485 TileSample sample = null; 486 int sampleI = (int)i/samples; 487 int sampleJ = (int)j/samples; 488 if (sampleI >= 0 && sampleI < sampleCache.length && 489 sampleJ >= 0 && sampleJ < sampleCache[0].length) { 490 sample = sampleCache[sampleI][sampleJ]; 491 if (sample != null) { 492 texIndex = sample.texIndex; 493 angle = sample.angle; 494 xOff = sample.xOff; 495 yOff = sample.yOff; 496 weight = sample.weight; 497 usingSample = true; 498 } 499 } 500 } 501 } 502 503 if (!usingSample) { 504 for (int m = 0; m < samples && i + m <= endColumn; m++) { 505 if (i + m < 0 || i + m >= tiles.length) continue; 506 for (int n = 0; n < samples && j + n < endRow; n++) { 507 if (j + n < 0 || j + n >= tiles[0].length) continue; 508 int currIndex = tiles[(int) i + m][(int) j + n]; 509 if (currIndex >= 0 && texIndex < 0) { 510 texIndex = currIndex; 511 Random rand = new Random((long) (i + j * tiles.length) * 1000000); 512 angle = rand.nextFloat() * 360f; 513 float offRange = renderSize * 0.25f; 514 xOff = -offRange / 2f + offRange * rand.nextFloat(); 515 yOff = -offRange / 2f + offRange * rand.nextFloat(); 516 // if (Keyboard.isKeyDown(Keyboard.KEY_O)) { 517 // xOff = yOff = 0f; 518 // } 519 } 520 if (currIndex >= 0) { 521 weight++; 522 } 523 } 524 } 525 } 526 527 if (texIndex >= 0) { 528 int texCellX = texIndex % params.tW; 529 int texCellY = texIndex / params.tW; 530 531 Color color = getRenderColor(); 532 float b = alphaMult * weight / max; 533// if (tiles.length > 30) { 534// b *= weight / max; 535// b *= weight / max; 536// } 537 //b = alphaMult; 538 GL11.glColor4ub((byte)color.getRed(), 539 (byte)color.getGreen(), 540 (byte)color.getBlue(), 541 (byte)((float)color.getAlpha() * b)); 542 //xOff = yOff = 0f; 543 renderQuad((int)i, (int)j, (x + xOff - w / 2f + i/samples * size + size/2f - renderSize/2f) * factor, 544 (y + yOff - h / 2f + j/samples * size + size/2f - renderSize/2f) * factor, 545 renderSize * factor, renderSize * factor, 546 texCellX * 0.25f, texCellY * 0.25f, 547 0.25f, 0.25f, 548 angle); 549 } 550 } 551 } 552 553 GL11.glEnd(); 554 } 555 } 556 557 558 protected float elapsed = 0f; 559 @Override 560 public void advance(float amount) { 561 super.advance(amount); 562// angleOffset += days * 20f; 563 float days = Global.getSector().getClock().convertToDays(amount); 564 elapsed += days; 565 } 566 567 protected void renderQuad(int i, int j, float x, float y, float width, float height, float texX, float texY, float texW, float texH, float angle) { 568 if (angle != 0) { 569 float vw = width / 2f; 570 float vh = height / 2f; 571 float cx = x + vw; 572 float cy = y + vh; 573 574 float cos = (float) Math.cos(angle * Misc.RAD_PER_DEG); 575 float sin = (float) Math.sin(angle * Misc.RAD_PER_DEG); 576 577 GL11.glTexCoord2f(texX, texY); 578 GL11.glVertex2f(cx + (-vw * cos + vh * sin), cy + (-vw * sin - vh * cos)); 579 580 GL11.glTexCoord2f(texX, texY + texH); 581 GL11.glVertex2f(cx + (-vw * cos - vh * sin), cy + (-vw * sin + vh * cos)); 582 583 GL11.glTexCoord2f(texX + texW, texY + texH); 584 GL11.glVertex2f(cx + (vw * cos - vh * sin), cy + (vw * sin + vh * cos)); 585 586 GL11.glTexCoord2f(texX + texW, texY); 587 GL11.glVertex2f(cx + (vw * cos + vh * sin), cy + (vw * sin - vh * cos)); 588 589 } else { 590 GL11.glTexCoord2f(texX, texY); 591 GL11.glVertex2f(x, y); 592 593 GL11.glTexCoord2f(texX, texY + texH); 594 GL11.glVertex2f(x, y + height); 595 596 GL11.glTexCoord2f(texX + texW, texY + texH); 597 GL11.glVertex2f(x + width, y + height); 598 599 GL11.glTexCoord2f(texX + texW, texY); 600 GL11.glVertex2f(x + width, y); 601 } 602 } 603 604 public float getMaxEffectRadius(Vector2f locFrom) { 605 // TODO: do intersection check from locFrom to an actual filled tile? 606 607 float size = getTileSize(); 608 float renderSize = getTileRenderSize(); 609 float w = tiles.length * size * 0.5f + (renderSize - size) * 0.5f; 610 float h = tiles[0].length * size * 0.5f + (renderSize - size) * 0.5f; 611 return Math.max(w, h) * 1.5f; 612 } 613 public float getMinEffectRadius(Vector2f locFrom) { 614 return 0f; 615 } 616 617 public float getOptimalEffectRadius(Vector2f locFrom) { 618 return getMaxEffectRadius(locFrom); 619 } 620 621 622 623 @Override 624 protected float getExtraSoundRadius() { 625 return 200f; 626 } 627 628 629 public float getProximitySoundFactor() { 630 CampaignFleetAPI player = Global.getSector().getPlayerFleet(); 631 float r = player.getRadius() + getExtraSoundRadius(); 632 Vector2f test = player.getLocation(); 633 634 float x = this.entity.getLocation().x; 635 float y = this.entity.getLocation().y; 636 float size = getTileSize(); 637 float containsSize = getTileContainsSize(); 638 639 float w = tiles.length * size; 640 float h = tiles[0].length * size; 641 642 x -= w/2f; 643 y -= h/2f; 644 645 float extra = (containsSize - size) / 2f; 646 647 if (test.x + r + extra < x) return 0f; 648 if (test.y + r + extra < y) return 0f; 649 if (test.x > x + w + r + extra) return 0f; 650 if (test.y > y + h + r + extra) return 0f; 651 652 int xIndex = (int) ((test.x - x) / size); 653 int yIndex = (int) ((test.y - y) / size); 654 655 if (xIndex < 0) xIndex = 0; 656 if (yIndex < 0) yIndex = 0; 657 658 if (xIndex >= tiles.length) xIndex = tiles.length - 1; 659 if (yIndex >= tiles[0].length) yIndex = tiles[0].length - 1; 660 661 662 float closestDist = Float.MAX_VALUE; 663 664 for (float i = Math.max(0, xIndex - 2); i <= xIndex + 2 && i < tiles.length; i++) { 665 for (float j = Math.max(0, yIndex - 2); j <= yIndex + 2 && j < tiles[0].length; j++) { 666 int texIndex = tiles[(int) i][(int) j]; 667 if (texIndex >= 0) { 668 float tx = x + i * size + size/2f - containsSize/2f; 669 float ty = y + j * size + size/2f - containsSize/2f; 670 671 if (test.x + r < tx) continue; 672 if (test.y + r < ty) continue; 673 if (test.x > tx + containsSize + r) continue; 674 if (test.y > ty + containsSize + r) continue; 675 676 //float dist = Misc.getDistance(test, new Vector2f(tx + containsSize/2f, ty + containsSize/2f)); 677 float dx = Math.abs(test.x - tx - containsSize / 2f); 678 float dy = Math.abs(test.y - ty - containsSize / 2f); 679 float dist = Math.max(dx, dy); 680 if (dist < closestDist) { 681 closestDist = dist; 682 } 683 } 684 } 685 } 686 687 //System.out.println("Closest: " + closestDist); 688 //float max = containsSize * 0.5f * 1.41f + EXTRA_SOUND_RADIUS; 689 float max = containsSize * 0.5f + getExtraSoundRadius(); 690 if (closestDist < containsSize * 0.5f) return 1f; 691 692 float p = (max - closestDist) / (max - containsSize * 0.5f); 693 if (p < 0) p = 0; 694 if (p > 1) p = 1; 695 696 return p; 697 } 698 699 700 701 public static String encodeTiles(int [][] tiles) { 702 int w = tiles.length; 703 int h = tiles[0].length; 704 int total = w * h; 705 706// int [] masks = new int [] { 707// 1, 708// 2, 709// 4, 710// 8, 711// 16, 712// 32, 713// 64, 714// 128, 715// }; 716 int [] masks = new int [] { 717 128, 718 64, 719 32, 720 16, 721 8, 722 4, 723 2, 724 1, 725 }; 726 727 int bit = 0; 728 int curr = 0; 729 //List<Byte> bytes = new ArrayList<Byte>(); 730 byte [] input = new byte [(int) Math.ceil(total / 8f)]; 731 for (int i = 0; i < total; i++) { 732 int x = i % w; 733 int y = i / w; 734 int val = tiles[x][y]; 735 int mask = masks[bit]; 736 737 if (val >= 0) { 738 curr = (curr | mask); 739 } 740 741 bit++; 742 bit %= 8; 743 744 if (bit == 0) { 745 input[i/8] = ((byte) curr); 746 curr = 0; 747 } 748 } 749 if (bit != 0) { 750 input[input.length - 1] = ((byte) curr); 751 curr = 0; 752 } 753 754 /* 755 List<Byte> bytes = new ArrayList<Byte>(); 756 String seq = ""; 757 for (int i = 0; i < total; i++) { 758 int x = i % w; 759 int y = i / w; 760 int val = tiles[x][y]; 761 String curr = "0"; 762 if (val >= 0) curr = "1"; 763 seq += curr; 764 if (seq.length() == 8) { 765 //byte b = Byte.parseByte(seq, 2); 766 int b = Integer.parseInt(seq, 2); 767 bytes.add((byte) b); 768 seq = ""; 769 } 770 } 771 if (seq.length() > 0) { 772 while (seq.length() < 8) { 773 seq = seq + "0"; 774 } 775 //byte b = Byte.parseByte(seq, 2); 776 //bytes.add(b); 777 int b = Integer.parseInt(seq, 2); 778 bytes.add((byte) b); 779 } 780 781 byte [] input = new byte [bytes.size()]; 782 for (int i = 0; i < bytes.size(); i++) { 783 input[i] = bytes.get(i); 784 } 785 */ 786 787 Deflater compressor = new Deflater(); 788 //compresser.setLevel(Deflater.BEST_COMPRESSION); 789 //compresser.setStrategy(Deflater.HUFFMAN_ONLY); 790 compressor.setInput(input); 791 compressor.finish(); 792 793 StringBuilder result = new StringBuilder(); 794 byte [] temp = new byte[100]; 795 796 while (!compressor.finished()) { 797 int read = compressor.deflate(temp); 798 result.append(toHexString(Arrays.copyOf(temp, read))); 799 } 800 801// result = new StringBuilder(); 802// result.append(toHexString(input)); 803 804 compressor.end(); 805 806 return result.toString(); 807 } 808 809 public static int [][] decodeTiles(String string, int w, int h) throws DataFormatException { 810 byte [] input = toByteArray(string); 811 812 Inflater decompressor = new Inflater(); 813 decompressor.setInput(input); 814 815 int [][] tiles = new int [w][h]; 816 int total = w * h; 817 int curr = 0; 818 819 byte [] temp = new byte[100]; 820 OUTER: while (!decompressor.finished()) { 821 int read = decompressor.inflate(temp); 822 for (int i = 0; i < read; i++) { 823 byte b = temp[i]; 824 825 for (int j = 7; j >= 0; j--) { 826 int x = curr % w; 827 int y = curr / w; 828 curr++; 829 830 if (curr > total) break OUTER; 831 //System.out.println("bytes read: " + curr); 832 if ((b & (0x01 << j)) > 0) { 833 tiles[x][y] = 1; 834 } else { 835 tiles[x][y] = -1; 836 } 837 } 838 } 839 } 840 841 decompressor.end(); 842 843 return tiles; 844 } 845 846 847 public static String toHexString(byte[] array) { 848 return DatatypeConverter.printBase64Binary(array); 849 } 850 851 public static byte[] toByteArray(String s) { 852 return DatatypeConverter.parseBase64Binary(s); 853 } 854 855 856 857 public static void main(String[] args) throws DataFormatException { 858 859// int [][] tiles = new int[][] { 860// {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 861// {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 862// {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 863// {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1}, 864// {1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 865// {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 866// {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 867// {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 868// {1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 869// {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 870// {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 871// {1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 872// {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 873// {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 874// {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 875// {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 876// {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1}, 877// {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 878// {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 879// {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 880// }; 881 882 int [][] tiles = new int[][] { 883 {-1, 1, 1, 1, -1}, 884 {1, 1, 1, 1, 1}, 885 {1, 1, 1, 1, -1}, 886 {1, 1, 1, -1, 1}, 887 }; 888 889// int w = 128; 890// int h = 128; 891// int [][] tiles = new int [w][h]; 892// 893// for (int i = 0; i < w; i++) { 894// for (int j = 0; j < h; j++) { 895// if ((float) Math.random() > 0.8f) { 896// tiles[i][j] = 0; 897// } else { 898// tiles[i][j] = -1; 899// } 900// } 901// } 902 903 904 System.out.println("Original:"); 905 for (int i = 0; i < tiles.length; i++) { 906 for (int j = 0; j < tiles[0].length; j++) { 907 System.out.print(String.format("% 2d,", tiles[i][j])); 908 } 909 System.out.println(); 910 } 911 912 String result = encodeTiles(tiles); 913 System.out.println(result); 914 //System.out.println(result.length() + ", would be " + (w * h / 4) + " without compression"); 915 int [][] tilesBack = decodeTiles(result, tiles.length, tiles[0].length); 916 917 System.out.println("Decoded:"); 918 for (int i = 0; i < tilesBack.length; i++) { 919 for (int j = 0; j < tilesBack[0].length; j++) { 920 System.out.print(String.format("% 2d,", tilesBack[i][j])); 921 } 922 System.out.println(); 923 } 924 925 boolean equals = true; 926 for (int i = 0; i < tiles.length; i++) { 927 for (int j = 0; j < tiles[0].length; j++) { 928 if (tiles[i][j] != tilesBack[i][j]) { 929 equals = false; 930 } 931 } 932 } 933 934 System.out.println("Equal: " + equals); 935 936// for (int x = 0; x < tilesBack.length; x++) { 937// for (int y = 0; y < tilesBack.length; y++) { 938// System.out.print(tilesBack[x][y] + " "); 939// } 940// System.out.println(); 941// } 942 } 943} 944 945 946 947 948 949 950