@Override public void addClientConfiguration(JSONObject mapObject) { s(mapObject, "perspective", name); s(mapObject, "azimuth", azimuth); s(mapObject, "inclination", inclination); s(mapObject, "scale", scale); s(mapObject, "worldtomap", world_to_map.toJSON()); s(mapObject, "maptoworld", map_to_world.toJSON()); int dir = ((360 + (int) (22.5 + azimuth)) / 45) % 8; ; s(mapObject, "compassview", directions[dir]); }
public IsoHDPerspective(ConfigurationNode configuration) { name = configuration.getString("name", null); if (name == null) { Log.severe("Perspective definition missing name - must be defined and unique"); return; } azimuth = configuration.getDouble("azimuth", 135.0); /* Get azimuth (default to classic kzed POV */ inclination = configuration.getDouble("inclination", 60.0); if (inclination > MAX_INCLINATION) inclination = MAX_INCLINATION; if (inclination < MIN_INCLINATION) inclination = MIN_INCLINATION; scale = configuration.getDouble("scale", MIN_SCALE); if (scale < MIN_SCALE) scale = MIN_SCALE; if (scale > MAX_SCALE) scale = MAX_SCALE; /* Get max and min height */ maxheight = configuration.getInteger("maximumheight", 127); if (maxheight > 127) maxheight = 127; minheight = configuration.getInteger("minimumheight", 0); if (minheight < 0) minheight = 0; /* Generate transform matrix for world-to-tile coordinate mapping */ /* First, need to fix basic coordinate mismatches before rotation - we want zero azimuth to have north to top * (world -X -> tile +Y) and east to right (world -Z to tile +X), with height being up (world +Y -> tile +Z) */ Matrix3D transform = new Matrix3D(0.0, 0.0, -1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0); /* Next, rotate world counterclockwise around Z axis by azumuth angle */ transform.rotateXY(180 - azimuth); /* Next, rotate world by (90-inclination) degrees clockwise around +X axis */ transform.rotateYZ(90.0 - inclination); /* Finally, shear along Z axis to normalize Z to be height above map plane */ transform.shearZ(0, Math.tan(Math.toRadians(90.0 - inclination))); /* And scale Z to be same scale as world coordinates, and scale X and Y based on setting */ transform.scale(scale, scale, Math.sin(Math.toRadians(inclination))); world_to_map = transform; /* Now, generate map to world tranform, by doing opposite actions in reverse order */ transform = new Matrix3D(); transform.scale(1.0 / scale, 1.0 / scale, 1 / Math.sin(Math.toRadians(inclination))); transform.shearZ(0, -Math.tan(Math.toRadians(90.0 - inclination))); transform.rotateYZ(-(90.0 - inclination)); transform.rotateXY(-180 + azimuth); Matrix3D coordswap = new Matrix3D(0.0, -1.0, 0.0, 0.0, 0.0, 1.0, -1.0, 0.0, 0.0); transform.multiply(coordswap); map_to_world = transform; /* Scaled models for non-cube blocks */ modscale = (int) Math.ceil(scale); scalemodels = HDBlockModels.getModelsForScale(modscale); ; }
@Override public MapTile[] getTiles(Location loc) { DynmapWorld world = MapManager.mapman.getWorld(loc.getWorld().getName()); HashSet<MapTile> tiles = new HashSet<MapTile>(); Vector3D block = new Vector3D(); block.setFromLocation(loc); /* Get coordinate for block */ Vector3D corner = new Vector3D(); /* Loop through corners of the cube */ for (int i = 0; i < 2; i++) { double inity = block.y; for (int j = 0; j < 2; j++) { double initz = block.z; for (int k = 0; k < 2; k++) { world_to_map.transform(block, corner); /* Get map coordinate of corner */ addTile( tiles, world, (int) Math.floor(corner.x / tileWidth), (int) Math.floor(corner.y / tileHeight)); block.z += 1; } block.z = initz; block.y += 1; } block.y = inity; block.x += 1; } return tiles.toArray(new MapTile[tiles.size()]); }
@Override public MapTile[] getTiles(Location loc0, Location loc1) { DynmapWorld world = MapManager.mapman.getWorld(loc0.getWorld().getName()); HashSet<MapTile> tiles = new HashSet<MapTile>(); Vector3D blocks[] = new Vector3D[] {new Vector3D(), new Vector3D()}; /* Get ordered point - 0=minX,Y,Z, 1=maxX,Y,Z */ if (loc0.getBlockX() < loc1.getBlockX()) { blocks[0].x = loc0.getBlockX(); blocks[1].x = loc1.getBlockX() + 1; } else { blocks[0].x = loc1.getBlockX(); blocks[1].x = loc0.getBlockX() + 1; } if (loc0.getBlockY() < loc1.getBlockY()) { blocks[0].y = loc0.getBlockY(); blocks[1].y = loc1.getBlockY() + 1; } else { blocks[0].y = loc1.getBlockY(); blocks[1].y = loc0.getBlockY() + 1; } if (loc0.getBlockZ() < loc1.getBlockZ()) { blocks[0].z = loc0.getBlockZ(); blocks[1].z = loc1.getBlockZ() + 1; } else { blocks[0].z = loc1.getBlockZ(); blocks[1].z = loc0.getBlockZ() + 1; } Vector3D corner = new Vector3D(); Vector3D tcorner = new Vector3D(); int mintilex = Integer.MAX_VALUE; int maxtilex = Integer.MIN_VALUE; int mintiley = Integer.MAX_VALUE; int maxtiley = Integer.MIN_VALUE; /* Loop through corners of the prism */ for (int i = 0; i < 2; i++) { corner.x = blocks[i].x; for (int j = 0; j < 2; j++) { corner.y = blocks[j].y; for (int k = 0; k < 2; k++) { corner.z = blocks[k].z; world_to_map.transform(corner, tcorner); /* Get map coordinate of corner */ int tx = (int) Math.floor(tcorner.x / tileWidth); int ty = (int) Math.floor(tcorner.y / tileWidth); if (mintilex > tx) mintilex = tx; if (maxtilex < tx) maxtilex = tx; if (mintiley > ty) mintiley = ty; if (maxtiley < ty) maxtiley = ty; } } } /* Now, add the tiles for the ranges - not perfect, but it works (some extra tiles on corners possible) */ for (int i = mintilex; i <= maxtilex; i++) { for (int j = mintiley; j < maxtiley; j++) { addTile(tiles, world, i, j); } } return tiles.toArray(new MapTile[tiles.size()]); }
@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; }
@Override public List<DynmapChunk> getRequiredChunks(MapTile tile) { if (!(tile instanceof HDMapTile)) return Collections.emptyList(); HDMapTile t = (HDMapTile) tile; int min_chunk_x = Integer.MAX_VALUE; int max_chunk_x = Integer.MIN_VALUE; int min_chunk_z = Integer.MAX_VALUE; int max_chunk_z = Integer.MIN_VALUE; /* Make corners for volume: 0 = bottom-lower-left, 1 = top-lower-left, 2=bottom-upper-left, 3=top-upper-left * 4 = bottom-lower-right, 5 = top-lower-right, 6 = bottom-upper-right, 7 = top-upper-right */ Vector3D corners[] = new Vector3D[8]; int[] chunk_x = new int[8]; int[] chunk_z = new int[8]; for (int x = t.tx, idx = 0; x <= (t.tx + 1); x++) { for (int y = t.ty; y <= (t.ty + 1); y++) { for (int z = 0; z <= 1; z++) { corners[idx] = new Vector3D(); corners[idx].x = x * tileWidth; corners[idx].y = y * tileHeight; corners[idx].z = z * 128; map_to_world.transform(corners[idx]); /* Compute chunk coordinates of corner */ chunk_x[idx] = (int) Math.floor(corners[idx].x / 16); chunk_z[idx] = (int) Math.floor(corners[idx].z / 16); /* Compute min/max of chunk coordinates */ if (min_chunk_x > chunk_x[idx]) min_chunk_x = chunk_x[idx]; if (max_chunk_x < chunk_x[idx]) max_chunk_x = chunk_x[idx]; if (min_chunk_z > chunk_z[idx]) min_chunk_z = chunk_z[idx]; if (max_chunk_z < chunk_z[idx]) max_chunk_z = chunk_z[idx]; idx++; } } } /* Make rectangles of X-Z projection of each side of the tile volume, 0 = top, 1 = bottom, 2 = left, 3 = right, * 4 = upper, 5 = lower */ Rectangle rect[] = new Rectangle[6]; rect[0] = new Rectangle(corners[1], corners[3], corners[5]); rect[1] = new Rectangle(corners[0], corners[2], corners[4]); rect[2] = new Rectangle(corners[0], corners[1], corners[2]); rect[3] = new Rectangle(corners[4], corners[5], corners[6]); rect[4] = new Rectangle(corners[2], corners[3], corners[6]); rect[5] = new Rectangle(corners[0], corners[1], corners[4]); /* Now, need to walk through the min/max range to see which chunks are actually needed */ ArrayList<DynmapChunk> chunks = new ArrayList<DynmapChunk>(); Rectangle chunkrect = new Rectangle(); int misscnt = 0; for (int x = min_chunk_x; x <= max_chunk_x; x++) { for (int z = min_chunk_z; z <= max_chunk_z; z++) { chunkrect.setSquare(x * 16, z * 16, 16); boolean hit = false; /* Check to see if square of chunk intersects any of our rectangle sides */ for (int rctidx = 0; (!hit) && (rctidx < rect.length); rctidx++) { if (chunkrect.testRectangleIntesectsRectangle(rect[rctidx])) { hit = true; } } if (hit) { DynmapChunk chunk = new DynmapChunk(x, z); chunks.add(chunk); } else { misscnt++; } } } return chunks; }