private final void shadowColor(Color c, int lightlevel, int[] shadowscale) { int scale = shadowscale[lightlevel]; if (scale < 256) c.setRGBA( (c.getRed() * scale) >> 8, (c.getGreen() * scale) >> 8, (c.getBlue() * scale) >> 8, c.getAlpha()); }
private void doScaleWithBilinear(int[] argb, int[] zargb, int width, int height) { Color c1 = new Color(); /* Now, compute zoomed tile - bilinear filter 2x2 -> 1x1 */ for (int y = 0; y < height; y += 2) { for (int x = 0; x < width; x += 2) { int red = 0; int green = 0; int blue = 0; int alpha = 0; for (int yy = y; yy < y + 2; yy++) { for (int xx = x; xx < x + 2; xx++) { c1.setARGB(argb[(yy * width) + xx]); red += c1.getRed(); green += c1.getGreen(); blue += c1.getBlue(); alpha += c1.getAlpha(); } } c1.setRGBA(red >> 2, green >> 2, blue >> 2, alpha >> 2); zargb[(y * width / 4) + (x / 2)] = c1.getARGB(); } } }
private void applySmoothLighting( HDPerspectiveState ps, HDShaderState ss, Color incolor, Color[] outcolor, int[] shadowscale) { int[] xyz = ps.getSubblockCoord(); int scale = (int) ps.getScale(); int mid = scale / 2; BlockStep s1, s2; int w1, w2; /* Figure out which directions to look */ switch (ps.getLastBlockStep()) { case X_MINUS: case X_PLUS: if (xyz[1] < mid) { s1 = BlockStep.Y_MINUS; w1 = mid - xyz[1]; } else { s1 = BlockStep.Y_PLUS; w1 = xyz[1] - mid; } if (xyz[2] < mid) { s2 = BlockStep.Z_MINUS; w2 = mid - xyz[2]; } else { s2 = BlockStep.Z_PLUS; w2 = xyz[2] - mid; } break; case Z_MINUS: case Z_PLUS: if (xyz[0] < mid) { s1 = BlockStep.X_MINUS; w1 = mid - xyz[0]; } else { s1 = BlockStep.X_PLUS; w1 = xyz[0] - mid; } if (xyz[1] < mid) { s2 = BlockStep.Y_MINUS; w2 = mid - xyz[1]; } else { s2 = BlockStep.Y_PLUS; w2 = xyz[1] - mid; } break; default: if (xyz[0] < mid) { s1 = BlockStep.X_MINUS; w1 = mid - xyz[0]; } else { s1 = BlockStep.X_PLUS; w1 = xyz[0] - mid; } if (xyz[2] < mid) { s2 = BlockStep.Z_MINUS; w2 = mid - xyz[2]; } else { s2 = BlockStep.Z_PLUS; w2 = xyz[2] - mid; } break; } /* Now get the 3 needed light levels */ LightLevels skyemit0 = ps.getCachedLightLevels(0); ps.getLightLevels(skyemit0); LightLevels skyemit1 = ps.getCachedLightLevels(1); ps.getLightLevelsAtStep(s1, skyemit1); LightLevels skyemit2 = ps.getCachedLightLevels(2); ps.getLightLevelsAtStep(s2, skyemit2); /* Get light levels */ int ll0 = getLightLevel(skyemit0, true); int ll1 = getLightLevel(skyemit1, true); int weight = 0; if (ll1 < ll0) weight -= w1; else if (ll1 > ll0) weight += w1; int ll2 = getLightLevel(skyemit2, true); if (ll2 < ll0) weight -= w2; else if (ll2 > ll0) weight += w2; outcolor[0].setColor(incolor); int cscale = 256; if (weight == 0) { cscale = shadowscale[ll0]; } else if (weight < 0) { /* If negative, interpolate down */ weight = -weight; if (ll0 > 0) { cscale = (shadowscale[ll0] * (scale - weight) + shadowscale[ll0 - 1] * weight) / scale; } else { cscale = shadowscale[ll0]; } } else { if (ll0 < 15) { cscale = (shadowscale[ll0] * (scale - weight) + shadowscale[ll0 + 1] * weight) / scale; } else { cscale = shadowscale[ll0]; } } if (cscale < 256) { Color c = outcolor[0]; c.setRGBA( (c.getRed() * cscale) >> 8, (c.getGreen() * cscale) >> 8, (c.getBlue() * cscale) >> 8, c.getAlpha()); } if (outcolor.length > 1) { ll0 = getLightLevel(skyemit0, false); ll1 = getLightLevel(skyemit1, false); weight = 0; if (ll1 < ll0) weight -= w1; else if (ll1 > ll0) weight += w1; ll2 = getLightLevel(skyemit2, false); if (ll2 < ll0) weight -= w2; else if (ll2 > ll0) weight += w2; outcolor[1].setColor(incolor); cscale = 256; if (weight == 0) { cscale = shadowscale[ll0]; } else if (weight < 0) { /* If negative, interpolate down */ weight = -weight; if (ll0 > 0) { cscale = (shadowscale[ll0] * (scale - weight) + shadowscale[ll0 - 1] * weight) / scale; } else { cscale = shadowscale[ll0]; } } else { if (ll0 < 15) { cscale = (shadowscale[ll0] * (scale - weight) + shadowscale[ll0 + 1] * weight) / scale; } else { cscale = shadowscale[ll0]; } } if (cscale < 256) { Color c = outcolor[1]; c.setRGBA( (c.getRed() * cscale) >> 8, (c.getGreen() * cscale) >> 8, (c.getBlue() * cscale) >> 8, c.getAlpha()); } } }
public boolean render(MapChunkCache cache, KzedMapTile tile, File outputFile) { World world = tile.getWorld(); boolean isnether = (world.getEnvironment() == Environment.NETHER); KzedBufferedImage im = KzedMap.allocateBufferedImage(KzedMap.tileWidth, KzedMap.tileHeight); KzedBufferedImage zim = KzedMap.allocateBufferedImage(KzedMap.tileWidth / 2, KzedMap.tileHeight / 2); boolean isempty = true; KzedBufferedImage im_day = null; KzedBufferedImage zim_day = null; if (night_and_day) { im_day = KzedMap.allocateBufferedImage(KzedMap.tileWidth, KzedMap.tileHeight); zim_day = KzedMap.allocateBufferedImage(KzedMap.tileWidth / 2, KzedMap.tileHeight / 2); } int ix = KzedMap.anchorx + tile.px / 2 + tile.py / 2 - ((127 - maximumHeight) / 2); int iy = maximumHeight; int iz = KzedMap.anchorz + tile.px / 2 - tile.py / 2 + ((127 - maximumHeight) / 2); /* Don't mess with existing height-clipped renders */ if (maximumHeight < 127) isnether = false; int jx, jz; int x, y; MapChunkCache.MapIterator mapiter = cache.getIterator(ix, iy, iz); Color c1 = new Color(); Color c2 = new Color(); int[] argb = im.argb_buf; int[] zargb = zim.argb_buf; Color c1_day = null; Color c2_day = null; int[] argb_day = null; int[] zargb_day = null; if (night_and_day) { c1_day = new Color(); c2_day = new Color(); argb_day = im_day.argb_buf; zargb_day = zim_day.argb_buf; } int rowoff = 0; /* draw the map */ for (y = 0; y < KzedMap.tileHeight; ) { jx = ix; jz = iz; for (x = KzedMap.tileWidth - 1; x >= 0; x -= 2) { mapiter.initialize(jx, iy, jz); scan(world, 0, isnether, c1, c1_day, mapiter); mapiter.initialize(jx, iy, jz); scan(world, 2, isnether, c2, c2_day, mapiter); argb[rowoff + x] = c1.getARGB(); argb[rowoff + x - 1] = c2.getARGB(); if (night_and_day) { argb_day[rowoff + x] = c1_day.getARGB(); argb_day[rowoff + x - 1] = c2_day.getARGB(); } isempty = isempty && c1.isTransparent() && c2.isTransparent(); jx++; jz++; } y++; rowoff += KzedMap.tileWidth; jx = ix; jz = iz - 1; for (x = KzedMap.tileWidth - 1; x >= 0; x -= 2) { mapiter.initialize(jx, iy, jz); scan(world, 2, isnether, c1, c1_day, mapiter); jx++; jz++; mapiter.initialize(jx, iy, jz); scan(world, 0, isnether, c2, c2_day, mapiter); argb[rowoff + x] = c1.getARGB(); argb[rowoff + x - 1] = c2.getARGB(); if (night_and_day) { argb_day[rowoff + x] = c1_day.getARGB(); argb_day[rowoff + x - 1] = c2_day.getARGB(); } isempty = isempty && c1.isTransparent() && c2.isTransparent(); } y++; rowoff += KzedMap.tileWidth; ix++; iz--; } /* Now, compute zoomed tile - bilinear filter 2x2 -> 1x1 */ doScaleWithBilinear(argb, zargb, KzedMap.tileWidth, KzedMap.tileHeight); if (night_and_day) { doScaleWithBilinear(argb_day, zargb_day, KzedMap.tileWidth, KzedMap.tileHeight); } /* Hand encoding and writing file off to MapManager */ KzedZoomedMapTile zmtile = new KzedZoomedMapTile(tile.getWorld(), (KzedMap) tile.getMap(), tile); File zoomFile = MapManager.mapman.getTileFile(zmtile); doFileWrites(outputFile, tile, im, im_day, zmtile, zoomFile, zim, zim_day, !isempty); return !isempty; }
protected void scan( World world, int seq, boolean isnether, final Color result, final Color result_day, MapChunkCache.MapIterator mapiter) { int lightlevel = 15; int lightlevel_day = 15; result.setTransparent(); if (result_day != null) result_day.setTransparent(); for (; ; ) { if (mapiter.y < 0) { return; } int id = mapiter.getBlockTypeID(); int data = 0; if (isnether) { /* Make bedrock ceiling into air in nether */ if (id != 0) { /* Remember first color we see, in case we wind up solid */ if (result.isTransparent()) if (colorScheme.colors[id] != null) result.setColor(colorScheme.colors[id][seq]); id = 0; } else isnether = false; } if (id != 0) { /* No update needed for air */ if (colorScheme.datacolors[id] != null) { /* If data colored */ data = mapiter.getBlockData(); } if ((shadowscale != null) && (mapiter.y < 127)) { /* Find light level of previous chunk */ switch (seq) { case 0: case 2: mapiter.incrementY(); break; case 1: mapiter.incrementX(); break; case 3: mapiter.decrementZ(); break; } lightlevel = lightlevel_day = mapiter.getBlockSkyLight(); if (lightscale != null) lightlevel = lightscale[lightlevel]; if ((lightlevel < 15) || (lightlevel_day < 15)) { int emitted = mapiter.getBlockEmittedLight(); lightlevel = Math.max(emitted, lightlevel); lightlevel_day = Math.max(emitted, lightlevel_day); } switch (seq) { case 0: case 2: mapiter.decrementY(); break; case 1: mapiter.decrementX(); break; case 3: mapiter.incrementZ(); break; } } } switch (seq) { case 0: mapiter.decrementX(); break; case 1: case 3: mapiter.decrementY(); break; case 2: mapiter.incrementZ(); break; } seq = (seq + 1) & 3; if (id != 0) { if (highlightBlocks.contains(id)) { result.setColor(highlightColor); return; } Color[] colors; if (data != 0) colors = colorScheme.datacolors[id][data]; else colors = colorScheme.colors[id]; if (colors != null) { Color c = colors[seq]; if (c.getAlpha() > 0) { /* we found something that isn't transparent, or not doing transparency */ if ((!transparency) || (c.getAlpha() == 255)) { /* it's opaque - the ray ends here */ result.setARGB(c.getARGB() | 0xFF000000); if (lightlevel < 15) { /* Not full light? */ shadowColor(result, lightlevel); } if (result_day != null) { if (lightlevel_day == lightlevel) /* Same light = same result */ result_day.setColor(result); else { result_day.setColor(c); if (lightlevel_day < 15) shadowColor(result_day, lightlevel_day); } } return; } /* this block is transparent, so recurse */ scan(world, seq, isnether, result, result_day, mapiter); int cr = c.getRed(); int cg = c.getGreen(); int cb = c.getBlue(); int ca = c.getAlpha(); if (lightlevel < 15) { int scale = shadowscale[lightlevel]; cr = (cr * scale) >> 8; cg = (cg * scale) >> 8; cb = (cb * scale) >> 8; } cr *= ca; cg *= ca; cb *= ca; int na = 255 - ca; result.setRGBA( (result.getRed() * na + cr) >> 8, (result.getGreen() * na + cg) >> 8, (result.getBlue() * na + cb) >> 8, 255); /* Handle day also */ if (result_day != null) { cr = c.getRed(); cg = c.getGreen(); cb = c.getBlue(); if (lightlevel_day < 15) { int scale = shadowscale[lightlevel_day]; cr = (cr * scale) >> 8; cg = (cg * scale) >> 8; cb = (cb * scale) >> 8; } cr *= ca; cg *= ca; cb *= ca; result_day.setRGBA( (result_day.getRed() * na + cr) >> 8, (result_day.getGreen() * na + cg) >> 8, (result_day.getBlue() * na + cb) >> 8, 255); } return; } } } } }
protected void scan( DynmapWorld world, int seq, boolean isnether, final Color result, final Color result_day, MapIterator mapiter) { int lightlevel = 15; int lightlevel_day = 15; BiomeMap bio = null; double rain = 0.0; double temp = 0.0; result.setTransparent(); if (result_day != null) result_day.setTransparent(); for (; ; ) { if (mapiter.getY() < 0) { return; } int id = mapiter.getBlockTypeID(); int data = 0; if (isnether) { /* Make bedrock ceiling into air in nether */ if (id != 0) { /* Remember first color we see, in case we wind up solid */ if (result.isTransparent()) { try { if (colorScheme.colors[id] != null) { result.setColor(colorScheme.colors[id][seq]); } } catch (ArrayIndexOutOfBoundsException aioobx) { colorScheme.resizeColorArray(id); } } id = 0; } else isnether = false; } if (id != 0) { /* No update needed for air */ switch (biomecolored) { case NONE: try { if (colorScheme.datacolors[id] != null) { /* If data colored */ data = mapiter.getBlockData(); } } catch (ArrayIndexOutOfBoundsException aioobx) { colorScheme.resizeColorArray(id); } break; case BIOME: bio = mapiter.getBiome(); break; case RAINFALL: rain = mapiter.getRawBiomeRainfall(); break; case TEMPERATURE: temp = mapiter.getRawBiomeTemperature(); break; } if ((shadowscale != null) && (mapiter.getY() < (mapiter.getWorldHeight() - 1))) { /* Find light level of previous chunk */ BlockStep last = mapiter.unstepPosition(); lightlevel = lightlevel_day = mapiter.getBlockSkyLight(); if (lightscale != null) lightlevel = lightscale[lightlevel]; if ((lightlevel < 15) || (lightlevel_day < 15)) { int emitted = mapiter.getBlockEmittedLight(); lightlevel = Math.max(emitted, lightlevel); lightlevel_day = Math.max(emitted, lightlevel_day); } mapiter.stepPosition(last); } } switch (seq) { case 0: mapiter.stepPosition(BlockStep.X_MINUS); break; case 1: case 3: mapiter.stepPosition(BlockStep.Y_MINUS); break; case 2: mapiter.stepPosition(BlockStep.Z_PLUS); break; } seq = (seq + 1) & 3; if (id != 0) { if (highlightBlocks.contains(id)) { result.setColor(highlightColor); return; } Color[] colors = null; switch (biomecolored) { case NONE: try { if (data != 0) colors = colorScheme.datacolors[id][data]; else colors = colorScheme.colors[id]; } catch (ArrayIndexOutOfBoundsException aioobx) { colorScheme.resizeColorArray(id); } break; case BIOME: if (bio != null) colors = colorScheme.biomecolors[bio.ordinal()]; break; case RAINFALL: colors = colorScheme.getRainColor(rain); break; case TEMPERATURE: colors = colorScheme.getTempColor(temp); break; } if (colors != null) { Color c = colors[seq]; if (c.getAlpha() > 0) { /* we found something that isn't transparent, or not doing transparency */ if ((!transparency) || (c.getAlpha() == 255)) { /* it's opaque - the ray ends here */ result.setARGB(c.getARGB() | 0xFF000000); if (lightlevel < 15) { /* Not full light? */ shadowColor(result, lightlevel); } if (result_day != null) { if (lightlevel_day == lightlevel) /* Same light = same result */ result_day.setColor(result); else { result_day.setColor(c); if (lightlevel_day < 15) shadowColor(result_day, lightlevel_day); } } return; } /* this block is transparent, so recurse */ scan(world, seq, isnether, result, result_day, mapiter); int cr = c.getRed(); int cg = c.getGreen(); int cb = c.getBlue(); int ca = c.getAlpha(); if (lightlevel < 15) { int scale = shadowscale[lightlevel]; cr = (cr * scale) >> 8; cg = (cg * scale) >> 8; cb = (cb * scale) >> 8; } cr *= ca; cg *= ca; cb *= ca; int na = 255 - ca; result.setRGBA( (result.getRed() * na + cr) >> 8, (result.getGreen() * na + cg) >> 8, (result.getBlue() * na + cb) >> 8, 255); /* Handle day also */ if (result_day != null) { cr = c.getRed(); cg = c.getGreen(); cb = c.getBlue(); if (lightlevel_day < 15) { int scale = shadowscale[lightlevel_day]; cr = (cr * scale) >> 8; cg = (cg * scale) >> 8; cb = (cb * scale) >> 8; } cr *= ca; cg *= ca; cb *= ca; result_day.setRGBA( (result_day.getRed() * na + cr) >> 8, (result_day.getGreen() * na + cg) >> 8, (result_day.getBlue() * na + cb) >> 8, 255); } return; } } } } }
@Override public boolean render(MapChunkCache cache, HDMapTile tile, String mapname) { Color rslt = new Color(); MapIterator mapiter = cache.getIterator(0, 0, 0); /* Build shader state object for each shader */ HDShaderState[] shaderstate = MapManager.mapman.hdmapman.getShaderStateForTile(tile, cache, mapiter, mapname); int numshaders = shaderstate.length; if (numshaders == 0) return false; /* Check if nether world */ boolean isnether = tile.getWorld().getEnvironment() == Environment.NETHER; /* Create buffered image for each */ DynmapBufferedImage im[] = new DynmapBufferedImage[numshaders]; DynmapBufferedImage dayim[] = new DynmapBufferedImage[numshaders]; int[][] argb_buf = new int[numshaders][]; int[][] day_argb_buf = new int[numshaders][]; for (int i = 0; i < numshaders; i++) { HDShader shader = shaderstate[i].getShader(); HDLighting lighting = shaderstate[i].getLighting(); if (shader.isEmittedLightLevelNeeded() || lighting.isEmittedLightLevelNeeded()) need_emittedlightlevel = true; if (shader.isSkyLightLevelNeeded() || lighting.isSkyLightLevelNeeded()) need_skylightlevel = true; im[i] = DynmapBufferedImage.allocateBufferedImage(tileWidth, tileHeight); argb_buf[i] = im[i].argb_buf; if (lighting.isNightAndDayEnabled()) { dayim[i] = DynmapBufferedImage.allocateBufferedImage(tileWidth, tileHeight); day_argb_buf[i] = dayim[i].argb_buf; } } /* Create perspective state object */ OurPerspectiveState ps = new OurPerspectiveState(mapiter, isnether); ps.top = new Vector3D(); ps.bottom = new Vector3D(); double xbase = tile.tx * tileWidth; double ybase = tile.ty * tileHeight; boolean shaderdone[] = new boolean[numshaders]; boolean rendered[] = new boolean[numshaders]; for (int x = 0; x < tileWidth; x++) { ps.px = x; for (int y = 0; y < tileHeight; y++) { ps.top.x = ps.bottom.x = xbase + x + 0.5; /* Start at center of pixel at Y=127.5, bottom at Y=-0.5 */ ps.top.y = ps.bottom.y = ybase + y + 0.5; ps.top.z = maxheight + 0.5; ps.bottom.z = minheight - 0.5; map_to_world.transform(ps.top); /* Transform to world coordinates */ map_to_world.transform(ps.bottom); ps.py = y; for (int i = 0; i < numshaders; i++) { shaderstate[i].reset(ps); } ps.raytrace(cache, mapiter, shaderstate, shaderdone); for (int i = 0; i < numshaders; i++) { if (shaderdone[i] == false) { shaderstate[i].rayFinished(ps); } else { shaderdone[i] = false; rendered[i] = true; } shaderstate[i].getRayColor(rslt, 0); argb_buf[i][(tileHeight - y - 1) * tileWidth + x] = rslt.getARGB(); if (day_argb_buf[i] != null) { shaderstate[i].getRayColor(rslt, 1); day_argb_buf[i][(tileHeight - y - 1) * tileWidth + x] = rslt.getARGB(); } } } } boolean renderone = false; /* Test to see if we're unchanged from older tile */ TileHashManager hashman = MapManager.mapman.hashman; for (int i = 0; i < numshaders; i++) { long crc = hashman.calculateTileHash(argb_buf[i]); boolean tile_update = false; String prefix = shaderstate[i].getMap().getPrefix(); if (rendered[i]) { renderone = true; MapType.ImageFormat fmt = shaderstate[i].getMap().getImageFormat(); String fname = tile.getFilename(prefix, fmt); File f = new File(tile.getDynmapWorld().worldtilepath, fname); FileLockManager.getWriteLock(f); try { if ((!f.exists()) || (crc != hashman.getImageHashCode(tile.getKey(), prefix, tile.tx, tile.ty))) { /* Wrap buffer as buffered image */ Debug.debug("saving image " + f.getPath()); if (!f.getParentFile().exists()) f.getParentFile().mkdirs(); try { FileLockManager.imageIOWrite(im[i].buf_img, fmt.getFileExt(), f); } catch (IOException e) { Debug.error("Failed to save image: " + f.getPath(), e); } catch (java.lang.NullPointerException e) { Debug.error("Failed to save image (NullPointerException): " + f.getPath(), e); } MapManager.mapman.pushUpdate(tile.getWorld(), new Client.Tile(fname)); hashman.updateHashCode(tile.getKey(), prefix, tile.tx, tile.ty, crc); tile.getDynmapWorld().enqueueZoomOutUpdate(f); tile_update = true; } else { Debug.debug("skipping image " + f.getPath() + " - hash match"); } } finally { FileLockManager.releaseWriteLock(f); DynmapBufferedImage.freeBufferedImage(im[i]); } MapManager.mapman.updateStatistics(tile, prefix, true, tile_update, !rendered[i]); /* Handle day image, if needed */ if (dayim[i] != null) { fname = tile.getDayFilename(prefix, fmt); f = new File(tile.getDynmapWorld().worldtilepath, fname); FileLockManager.getWriteLock(f); prefix = prefix + "_day"; tile_update = false; try { if ((!f.exists()) || (crc != hashman.getImageHashCode(tile.getKey(), prefix, tile.tx, tile.ty))) { /* Wrap buffer as buffered image */ Debug.debug("saving image " + f.getPath()); if (!f.getParentFile().exists()) f.getParentFile().mkdirs(); try { FileLockManager.imageIOWrite(dayim[i].buf_img, fmt.getFileExt(), f); } catch (IOException e) { Debug.error("Failed to save image: " + f.getPath(), e); } catch (java.lang.NullPointerException e) { Debug.error("Failed to save image (NullPointerException): " + f.getPath(), e); } MapManager.mapman.pushUpdate(tile.getWorld(), new Client.Tile(fname)); hashman.updateHashCode(tile.getKey(), prefix, tile.tx, tile.ty, crc); tile.getDynmapWorld().enqueueZoomOutUpdate(f); tile_update = true; } else { Debug.debug("skipping image " + f.getPath() + " - hash match"); } } finally { FileLockManager.releaseWriteLock(f); DynmapBufferedImage.freeBufferedImage(dayim[i]); } MapManager.mapman.updateStatistics(tile, prefix, true, tile_update, !rendered[i]); } } } return renderone; }