-
-
Save Hadyn/0a445dff0961eac9c6f3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.github.hadyn.api.tool; | |
import net.openrs.cache.Cache; | |
import net.openrs.cache.Container; | |
import net.openrs.cache.FileStore; | |
import net.openrs.cache.ReferenceTable; | |
import org.github.hadyn.api.cache.OverlayFloorDefinition; | |
import org.github.hadyn.api.cache.OverlayFloorDefinitions; | |
import org.github.hadyn.api.cache.UnderlayFloorConfig; | |
import org.github.hadyn.api.cache.UnderlayFloorDefinitions; | |
import javax.imageio.ImageIO; | |
import java.awt.*; | |
import java.awt.geom.AffineTransform; | |
import java.awt.image.BufferedImage; | |
import java.io.FileOutputStream; | |
import java.io.IOException; | |
import java.nio.ByteBuffer; | |
import java.util.ArrayList; | |
import java.util.List; | |
/** | |
* @author Hadyn Fitzgerald | |
*/ | |
public class MapRenderer { | |
/** | |
* The tile shapes as bit maps. | |
*/ | |
private static int[][] TILE_SHAPES = new int[][]{ | |
{ | |
0, 0, 0, 0, | |
0, 0, 0, 0, | |
0, 0, 0, 0, | |
0, 0, 0, 0 | |
}, | |
{ | |
1, 1, 1, 1, | |
1, 1, 1, 1, | |
1, 1, 1, 1, | |
1, 1, 1, 1 | |
}, | |
{ | |
1, 0, 0, 0, | |
1, 1, 0, 0, | |
1, 1, 1, 0, | |
1, 1, 1, 1 | |
}, | |
{ | |
1, 1, 0, 0, | |
1, 1, 0, 0, | |
1, 0, 0, 0, | |
1, 0, 0, 0 | |
}, | |
{ | |
0, 0, 1, 1, | |
0, 0, 1, 1, | |
0, 0, 0, 1, | |
0, 0, 0, 1}, | |
{ | |
0, 1, 1, 1, | |
0, 1, 1, 1, | |
1, 1, 1, 1, | |
1, 1, 1, 1 | |
}, | |
{ | |
1, 1, 1, 0, | |
1, 1, 1, 0, | |
1, 1, 1, 1, | |
1, 1, 1, 1 | |
}, | |
{ | |
1, 1, 0, 0, | |
1, 1, 0, 0, | |
1, 1, 0, 0, | |
1, 1, 0, 0 | |
}, | |
{ | |
0, 0, 0, 0, | |
0, 0, 0, 0, | |
1, 0, 0, 0, | |
1, 1, 0, 0 | |
}, | |
{ | |
1, 1, 1, 1, | |
1, 1, 1, 1, | |
0, 1, 1, 1, | |
0, 0, 1, 1 | |
}, | |
{ | |
1, 1, 1, 1, | |
1, 1, 0, 0, | |
1, 0, 0, 0, | |
1, 0, 0, 0 | |
}, | |
{ | |
0, 0, 0, 0, | |
0, 0, 1, 1, | |
0, 1, 1, 1, | |
0, 1, 1, 1 | |
}, | |
{ | |
0, 0, 0, 0, | |
0, 0, 0, 0, | |
0, 1, 1, 0, | |
1, 1, 1, 1 | |
} | |
}; | |
/** | |
* The tile rotation matrices for each shape. | |
*/ | |
private static int[][] TILE_ROTATIONS = new int[][]{ | |
{ | |
0, 1, 2, 3, | |
4, 5, 6, 7, | |
8, 9, 10, 11, | |
12, 13, 14, 15 | |
}, | |
{ | |
12, 8, 4, 0, | |
13, 9, 5, 1, | |
14, 10, 6, 2, | |
15, 11, 7, 3 | |
}, | |
{ | |
15, 14, 13, 12, | |
11, 10, 9, 8, | |
7, 6, 5, 4, | |
3, 2, 1, 0 | |
}, | |
{ | |
3, 7, 11, 15, | |
2, 6, 10, 14, | |
1, 5, 9, 13, | |
0, 4, 8, 12 | |
} | |
}; | |
class RenderableTile { | |
private int x; | |
private int y; | |
private int shape; | |
private int rotation; | |
private int backgroundColor; | |
private int foregroundColor; | |
public RenderableTile(int x, int y, int shape, int rotation, int backgroundColor, int foregroundColor) { | |
this.x = x; | |
this.y = y; | |
this.shape = shape; | |
this.rotation = rotation; | |
this.backgroundColor = backgroundColor; | |
this.foregroundColor = foregroundColor; | |
} | |
/** | |
* Draws to a buffered image. Assumes that the image we are drawing to will accept a 4 x 4 pixel area to draw | |
* the tile to at the respected x and y coordinate for this tile. This method draws respective to the bottom | |
* left y axis. | |
* | |
* @param image the image to draw to. | |
*/ | |
public void drawTo(BufferedImage image) { | |
if (shape > 0) { | |
int[] shapeMatrix = TILE_SHAPES[shape]; | |
int[] rotationMatrix = TILE_ROTATIONS[rotation]; | |
if (backgroundColor != 0) { | |
for (int oy = 0; oy < 4; oy++) { | |
for (int ox = 0; ox < 4; ox++) { | |
int drawx = x * 4 + ox; | |
int drawy = y * 4 + oy; | |
image.setRGB(drawx, drawy, shapeMatrix[rotationMatrix[ox + (3-oy) * 4]] == 0 ? backgroundColor : foregroundColor); | |
} | |
} | |
} else { | |
for (int oy = 0; oy < 4; oy++) { | |
for (int ox = 0; ox < 4; ox++) { | |
int drawx = x * 4 + ox; | |
int drawy = y * 4 + oy; | |
if (shapeMatrix[rotationMatrix[ox + (3-oy) * 4]] != 0) { | |
image.setRGB(drawx, drawy, foregroundColor); | |
} | |
} | |
} | |
} | |
} else { | |
for (int oy = 0; oy < 4; oy++) { | |
for (int ox = 0; ox < 4; ox++) { | |
int drawx = x * 4 + ox; | |
int drawy = y * 4 + oy; | |
image.setRGB(drawx, drawy, backgroundColor); | |
} | |
} | |
} | |
} | |
} | |
class MapBlock { | |
/** | |
* The width. | |
*/ | |
int width; | |
/** | |
* The height. | |
*/ | |
int height; | |
/** | |
* The underlay floor ids. | |
*/ | |
int[][][] foregroundConfigIds; | |
/** | |
* The overlay floor ids, tile shapes, and rotations. | |
*/ | |
int[][][] backgroundConfigIds; | |
int[][][] tileShapes; | |
int[][][] tileRotations; | |
/** | |
* The accumulated hue, saturation, luminosity, and intensity for each y coordinate. | |
*/ | |
int[] accHue; | |
int[] accSaturation; | |
int[] accLuminosity; | |
int[] accIntensity; | |
int[] counter; | |
/** | |
* The tile flags. | |
*/ | |
int[][][] flags; | |
MapBlock(int width, int height) { | |
this.width = width; | |
this.height = height; | |
foregroundConfigIds = new int[4][width][height]; | |
backgroundConfigIds = new int[4][width][height]; | |
tileShapes = new int[4][width][height]; | |
tileRotations = new int[4][width][height]; | |
accHue = new int[height]; | |
accLuminosity = new int[height]; | |
accSaturation = new int[height]; | |
accIntensity = new int[height]; | |
counter = new int[height]; | |
flags = new int[4][width][height]; | |
} | |
void decode(ByteBuffer buffer) { | |
for (int plane = 0; plane < 4; plane++) { | |
for (int x = 0; x < 64; x++) { | |
for (int y = 0; y < 64; y++) { | |
for (; ; ) { | |
int config = buffer.get() & 0xFF; | |
if (config == 0) { | |
break; | |
} else if (config == 1) { | |
buffer.get(); | |
break; | |
} else if (config <= 49) { | |
foregroundConfigIds[plane][x][y] = buffer.get() & 0xff; | |
tileShapes[plane][x][y] = (config - 2) / 4; | |
tileRotations[plane][x][y] = (config - 2 & 3); | |
} else if (config <= 81) { | |
flags[plane][x][y] = config - 49; | |
} else { | |
backgroundConfigIds[plane][x][y] = config - 81; | |
} | |
} | |
} | |
} | |
} | |
} | |
List<RenderableTile> getRenderableTiles(int targetPlane) throws IOException { | |
List<RenderableTile> renderableTiles = new ArrayList<>(); | |
for (int plane = 0; plane < 4; plane++) { | |
for (int x = -5; x < width + 5; x++) { | |
for (int y = 0; y < height; y++) { | |
int accSample = x + 5; | |
if (accSample >= 0 && accSample < width) { | |
int configId = backgroundConfigIds[plane][accSample][y]; | |
if (configId != 0) { | |
UnderlayFloorConfig definition = UnderlayFloorDefinitions.forId(configId - 1); | |
definition.init(); | |
accHue[y] += definition.getHue(); | |
accSaturation[y] += definition.getSaturation(); | |
accLuminosity[y] += definition.getLuminosity(); | |
accIntensity[y] += definition.getIntensity(); | |
counter[y]++; | |
} | |
} | |
int subSample = x - 5; | |
if (subSample >= 0 && subSample < width) { | |
int configId = backgroundConfigIds[plane][subSample][y]; | |
if (configId != 0) { | |
UnderlayFloorConfig definition = UnderlayFloorDefinitions.forId(configId - 1); | |
definition.init(); | |
accHue[y] -= definition.getHue(); | |
accSaturation[y] -= definition.getSaturation(); | |
accLuminosity[y] -= definition.getLuminosity(); | |
accIntensity[y] -= definition.getIntensity(); | |
counter[y]--; | |
} | |
} | |
} | |
if (x >= 1 && x < width - 1) { | |
int hue = 0; | |
int sat = 0; | |
int lum = 0; | |
int intensity = 0; | |
int total = 0; | |
for (int y = -5; y < height + 5; y++) { | |
int accSample = y + 5; | |
if (accSample >= 0 && accSample < height) { | |
hue += accHue[accSample]; | |
sat += accSaturation[accSample]; | |
lum += accLuminosity[accSample]; | |
intensity += accIntensity[accSample]; | |
total += counter[accSample]; | |
} | |
int subSample = y - 5; | |
if (subSample >= 0 && subSample < height) { | |
hue -= accHue[subSample]; | |
sat -= accSaturation[subSample]; | |
lum -= accLuminosity[subSample]; | |
intensity -= accIntensity[subSample]; | |
total -= counter[subSample]; | |
} | |
if (plane != targetPlane) { | |
continue; | |
} | |
if (y >= 1 && y < height - 1) { | |
int backgroundConfigId = backgroundConfigIds[plane][x][y]; | |
int foregroundConfigId = foregroundConfigIds[plane][x][y]; | |
if (backgroundConfigId > 0 || foregroundConfigId > 0) { | |
int packedBackgroundColor = -1; | |
if (backgroundConfigId > 0) { | |
int h = hue * 256 / intensity; | |
int s = sat / total; | |
int l = lum / total; | |
if (l < 0) { | |
l = 0; | |
} else if (l > 255) { | |
l = 255; | |
} | |
packedBackgroundColor = getHslToInt16(h, s, l); | |
} | |
int backgroundColor = 0; | |
if (packedBackgroundColor != -1) { | |
backgroundColor = int16RgbTable[method622(packedBackgroundColor, 96)]; | |
} | |
if (foregroundConfigId == 0) { | |
renderableTiles.add(new RenderableTile(x, y, 0, 0, backgroundColor, 0)); | |
} else { | |
int shape = tileShapes[plane][x][y] + 1; | |
int rotation = tileRotations[plane][x][y]; | |
OverlayFloorDefinition floor = OverlayFloorDefinitions.forId(foregroundConfigId - 1); | |
floor.init(); | |
int packedForegroundColor = -1; | |
if (false) { | |
// TODO(sinisoul): Textures | |
} else if (floor.getRgbColor() == 0xff00ff) { | |
packedForegroundColor = -2; | |
} else { | |
packedForegroundColor = getHslToInt16(floor.getHue(), floor.getSaturation(), floor.getLuminosity()); | |
} | |
int foregroundColor = 0; | |
if (packedForegroundColor != -2) { | |
foregroundColor = int16RgbTable[method1927(packedForegroundColor, 96)]; | |
} | |
renderableTiles.add(new RenderableTile(x, y, shape, rotation, backgroundColor, foregroundColor)); | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
return renderableTiles; | |
} | |
} | |
static final int method622(int var0, int var1) { | |
if (-1 == var0) { | |
return 12345678; | |
} else { | |
var1 = (var0 & 127) * var1 / 128; | |
if (var1 >= 2) { | |
if (var1 > 126) { | |
var1 = 126; | |
} | |
} else { | |
var1 = 2; | |
} | |
return var1 + (var0 & '\uff80'); | |
} | |
} | |
static final int method1927(int var0, int var1) { | |
if(-2 == var0) { | |
return 12345678; | |
} else if(var0 == -1) { | |
if(var1 < 2) { | |
var1 = 2; | |
} else if(var1 > 126) { | |
var1 = 126; | |
} | |
return var1; | |
} else { | |
var1 = var1 * (var0 & 127) / 128; | |
if(var1 < 2) { | |
var1 = 2; | |
} else if(var1 > 126) { | |
var1 = 126; | |
} | |
return (var0 & '\uff80') + var1; | |
} | |
} | |
/** | |
* | |
*/ | |
private Cache cache; | |
/** | |
* | |
*/ | |
private ReferenceTable table; | |
/** | |
* @param cache | |
* @throws IOException | |
*/ | |
public MapRenderer(Cache cache) throws IOException { | |
this.cache = cache; | |
table = ReferenceTable.decode(Container.decode(cache.getStore().read(255, 5)).getData()); | |
} | |
/** | |
* @param x | |
* @param y | |
*/ | |
public BufferedImage render(int plane, int x, int y) throws IOException { | |
int landscapeEntryId = table.findEntry("l" + x + "_" + y); | |
int mapEntryId = table.findEntry("m" + x + "_" + y); | |
if (landscapeEntryId == -1 || mapEntryId == -1) { | |
return null; | |
} | |
MapBlock block = new MapBlock(64, 64); | |
block.decode(cache.read(5, mapEntryId).getData()); | |
BufferedImage bufferedImage = new BufferedImage(64 * 4, 64 * 4, BufferedImage.TYPE_INT_RGB); | |
List<RenderableTile> tiles = block.getRenderableTiles(plane); | |
tiles.forEach(tile -> tile.drawTo(bufferedImage)); | |
BufferedImage copy = new BufferedImage(64 * 4, 64 * 4, BufferedImage.TYPE_INT_RGB); | |
Graphics2D g = copy.createGraphics(); | |
AffineTransform at = new AffineTransform(); | |
at.concatenate(AffineTransform.getScaleInstance(1, -1)); | |
at.concatenate(AffineTransform.getTranslateInstance(0, -bufferedImage.getHeight())); | |
g.transform(at); | |
g.drawImage(bufferedImage, 0, 0, null); | |
g.dispose(); | |
return copy; | |
} | |
private static final int getHslToInt16(int hue, int saturation, int luminosity) { | |
if (luminosity > 179) { | |
saturation /= 2; | |
} | |
if (luminosity > 192) { | |
saturation /= 2; | |
} | |
if (luminosity > 217) { | |
saturation /= 2; | |
} | |
if (luminosity > 243) { | |
saturation /= 2; | |
} | |
int var4 = (hue / 4 << 10) + (saturation / 32 << 7) + luminosity / 2; | |
return var4; | |
} | |
private static int[] int16RgbTable = new int[65536]; | |
public static final void calculatePalette(double var0, int var2, int var3) { | |
var0 += Math.random() * 0.03D - 0.015D; | |
int var21 = var2 * 128; | |
for (int var13 = var2; var13 < var3; ++var13) { | |
double var24 = (double) (var13 >> 3) / 64.0D + 0.0078125D; | |
double var22 = (double) (var13 & 7) / 8.0D + 0.0625D; | |
for (int var16 = 0; var16 < 128; ++var16) { | |
double var7 = (double) var16 / 128.0D; | |
double var19 = var7; | |
double var4 = var7; | |
double var14 = var7; | |
if (var22 != 0.0D) { | |
double var9; | |
if (var7 < 0.5D) { | |
var9 = var7 * (1.0D + var22); | |
} else { | |
var9 = var7 + var22 - var7 * var22; | |
} | |
double var11 = 2.0D * var7 - var9; | |
double var17 = var24 + 0.3333333333333333D; | |
if (var17 > 1.0D) { | |
--var17; | |
} | |
double var28 = var24 - 0.3333333333333333D; | |
if (var28 < 0.0D) { | |
++var28; | |
} | |
if (6.0D * var17 < 1.0D) { | |
var19 = var11 + (var9 - var11) * 6.0D * var17; | |
} else if (2.0D * var17 < 1.0D) { | |
var19 = var9; | |
} else if (3.0D * var17 < 2.0D) { | |
var19 = var11 + (var9 - var11) * (0.6666666666666666D - var17) * 6.0D; | |
} else { | |
var19 = var11; | |
} | |
if (6.0D * var24 < 1.0D) { | |
var4 = var11 + (var9 - var11) * 6.0D * var24; | |
} else if (2.0D * var24 < 1.0D) { | |
var4 = var9; | |
} else if (3.0D * var24 < 2.0D) { | |
var4 = var11 + (var9 - var11) * (0.6666666666666666D - var24) * 6.0D; | |
} else { | |
var4 = var11; | |
} | |
if (6.0D * var28 < 1.0D) { | |
var14 = var11 + (var9 - var11) * 6.0D * var28; | |
} else if (2.0D * var28 < 1.0D) { | |
var14 = var9; | |
} else if (3.0D * var28 < 2.0D) { | |
var14 = var11 + (var9 - var11) * (0.6666666666666666D - var28) * 6.0D; | |
} else { | |
var14 = var11; | |
} | |
} | |
int var31 = (int) (var19 * 256.0D); | |
int var6 = (int) (var4 * 256.0D); | |
int var32 = (int) (var14 * 256.0D); | |
int var30 = (var31 << 16) + (var6 << 8) + var32; | |
var30 = adjustBrightness(var30, var0); | |
if (var30 == 0) { | |
var30 = 1; | |
} | |
int16RgbTable[var21++] = var30; | |
} | |
} | |
} | |
private static int adjustBrightness(int rgb, double brightness) { | |
double r = (double) (rgb >> 16) / 256.0D; | |
double g = (double) (rgb >> 8 & 255) / 256.0D; | |
double b = (double) (rgb & 255) / 256.0D; | |
r = Math.pow(r, brightness); | |
g = Math.pow(g, brightness); | |
b = Math.pow(b, brightness); | |
int rBright = (int) (r * 256.0D); | |
int gBRight = (int) (g * 256.0D); | |
int bBright = (int) (b * 256.0D); | |
return (rBright << 16) + (gBRight << 8) + bBright; | |
} | |
/** | |
* Renders the runescape map out into region by region pictures. | |
* | |
* @param args the command line arguments. | |
*/ | |
public static void main(String... args) throws IOException { | |
calculatePalette(0.5, 0, 512); | |
Cache cache = new Cache(FileStore.open("data/")); | |
UnderlayFloorDefinitions.init(cache); | |
OverlayFloorDefinitions.init(cache); | |
MapRenderer renderer = new MapRenderer(cache); | |
ImageIO.write(renderer.render(0, 50, 50), "png", new FileOutputStream("render.png")); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment