diff --git a/build.gradle b/build.gradle index 3ae50cb20..fe7301f28 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { } group 'com.volmit.iris' -version '1.5-TOTALLY-UNSTABLE' +version '1.5-UNSTABLE-MAP' def apiVersion = '1.17' def name = 'Iris' def main = 'com.volmit.iris.Iris' diff --git a/src/main/java/com/volmit/iris/generator/IrisComplex.java b/src/main/java/com/volmit/iris/generator/IrisComplex.java index 7d3e434c6..689c1615d 100644 --- a/src/main/java/com/volmit/iris/generator/IrisComplex.java +++ b/src/main/java/com/volmit/iris/generator/IrisComplex.java @@ -96,6 +96,10 @@ public class IrisComplex implements DataProvider { } public IrisComplex(Engine engine) { + this(engine, false); + } + + public IrisComplex(Engine engine, boolean simple) { int cacheSize = IrisSettings.get().getCache().getStreamingCacheSize(); this.rng = new RNG(engine.getWorld().getSeed()); this.data = engine.getData(); diff --git a/src/main/java/com/volmit/iris/manager/command/studio/CommandIrisStudioMap.java b/src/main/java/com/volmit/iris/manager/command/studio/CommandIrisStudioMap.java index 8f2e12a48..fab0912b8 100644 --- a/src/main/java/com/volmit/iris/manager/command/studio/CommandIrisStudioMap.java +++ b/src/main/java/com/volmit/iris/manager/command/studio/CommandIrisStudioMap.java @@ -20,15 +20,24 @@ package com.volmit.iris.manager.command.studio; import com.volmit.iris.Iris; import com.volmit.iris.IrisSettings; -import com.volmit.iris.manager.gui.IrisVision; -import com.volmit.iris.scaffold.IrisWorlds; +import com.volmit.iris.generator.IrisComplex; +import com.volmit.iris.manager.IrisDataManager; +import com.volmit.iris.map.MapVision; +import com.volmit.iris.object.IrisDimension; import com.volmit.iris.scaffold.engine.IrisAccess; +import com.volmit.iris.util.FakeEngine; +import com.volmit.iris.util.FakeWorld; import com.volmit.iris.util.KList; import com.volmit.iris.util.MortarCommand; import com.volmit.iris.util.MortarSender; +import org.bukkit.World; -public class CommandIrisStudioMap extends MortarCommand { - public CommandIrisStudioMap() { +import java.io.File; + +public class CommandIrisStudioMap extends MortarCommand +{ + public CommandIrisStudioMap() + { super("map", "render"); setDescription("Render a map (gui outside of mc)"); requiresPermission(Iris.perm.studio); @@ -41,17 +50,61 @@ public class CommandIrisStudioMap extends MortarCommand { } @Override - public boolean handle(MortarSender sender, String[] args) { - if (!IrisSettings.get().isStudio()) { + public boolean handle(MortarSender sender, String[] args) + { + if(!IrisSettings.get().isStudio()) + { sender.sendMessage("To use Iris Studio, please enable studio in Iris/settings.json"); return true; } - if (!IrisSettings.get().isUseServerLaunchedGuis()) { + if(!IrisSettings.get().isUseServerLaunchedGuis()) + { sender.sendMessage("To use Iris Guis, please enable serverLaunchedGuis in Iris/settings.json"); return true; } + IrisComplex complex; + + if (args.length > 0) { + String type = ""; + long seed = 1337; + for(String i : args) + { + if (i.contains("=")) { + type = i.startsWith("type=") ? i.split("\\Q=\\E")[1] : type; + seed = i.startsWith("seed=") ? Long.valueOf(i.split("\\Q=\\E")[1]) : seed; + } else { + if (type.equals("")) { + type = i; + } else if (seed == 1337) { + seed = Long.valueOf(i); + } + } + } + + if (type.equals("")) { + sender.sendMessage("Open this in a studio world or do /iris studio map [pack]"); + return true; + } + + IrisDimension dim = IrisDataManager.loadAnyDimension(type); + + if (dim == null) { + sender.sendMessage("Can't find dimension: " + type); + return true; + } + + if (dim.getEnvironment() == null) { + dim.setEnvironment(World.Environment.NORMAL); + } + + //Setup the fake world and engine objects so we can get an IrisComplex for the terrain they will + //generate without actually generating any of it + sender.sendMessage("Preparing map..."); + FakeWorld world = new FakeWorld(dim.getName(), 0, 256, seed, new File(dim.getName()), dim.getEnvironment()); + FakeEngine engine = new FakeEngine(dim, world); + complex = new IrisComplex(engine, true); try { IrisAccess g = Iris.proj.getActiveProject().getActiveProvider(); @@ -62,13 +115,23 @@ public class CommandIrisStudioMap extends MortarCommand { IrisAccess g = IrisWorlds.access(sender.player().getWorld()); IrisVision.launch(g, 0); sender.sendMessage("Opening Map!"); + } else if (Iris.proj.isProjectOpen()) { + IrisAccess g = Iris.proj.getActiveProject().getActiveProvider(); + complex = g.getCompound().getDefaultEngine().getFramework().getComplex(); + sender.sendMessage("Opening map for existing studio world!"); + } else { + sender.sendMessage("Open this in a studio world or do /iris studio map [pack]"); + return true; } + MapVision map = new MapVision(complex); + map.open(); return true; } @Override - protected String getArgsUsage() { - return ""; + protected String getArgsUsage() + { + return "[pack] [seed=1337]"; } } diff --git a/src/main/java/com/volmit/iris/map/BiomeMap.java b/src/main/java/com/volmit/iris/map/BiomeMap.java new file mode 100644 index 000000000..694662e2f --- /dev/null +++ b/src/main/java/com/volmit/iris/map/BiomeMap.java @@ -0,0 +1,67 @@ +package com.volmit.iris.map; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.volmit.iris.object.IrisBiome; +import com.volmit.iris.object.IrisDimension; +import com.volmit.iris.object.IrisRegion; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class BiomeMap { + + private BiMap biomeMap; + private BiMap regionMap; + private IrisDimension dimension; + + private Set activeBiomes = new HashSet<>(); + private Set activeRegions = new HashSet<>(); + + public BiomeMap(IrisDimension dimension) { + this.dimension = dimension; + + List biomes = dimension.getAllAnyBiomes(); + List regions = dimension.getAllAnyRegions(); + + biomeMap = HashBiMap.create(biomes.size()); + regionMap = HashBiMap.create(regions.size()); + + int nextID = 0; + + for (IrisBiome biome : biomes) { + biomeMap.putIfAbsent(biome, nextID); + activeBiomes.add(nextID); + nextID++; + } + + nextID = 0; + + for (IrisRegion region : regions) { + regionMap.putIfAbsent(region, nextID); + activeRegions.add(nextID); + nextID++; + } + } + + public IrisDimension getDimension() { + return dimension; + } + + public IrisBiome getBiome(int id) { + return biomeMap.inverse().get(id); + } + + public int getBiomeId(IrisBiome biome) { + return biomeMap.get(biome); + } + + public IrisRegion getRegion(int id) { + return regionMap.inverse().get(id); + } + + public int getRegionId(IrisRegion region) { + return regionMap.get(region); + } +} diff --git a/src/main/java/com/volmit/iris/map/MapVision.java b/src/main/java/com/volmit/iris/map/MapVision.java new file mode 100644 index 000000000..17b18b8ed --- /dev/null +++ b/src/main/java/com/volmit/iris/map/MapVision.java @@ -0,0 +1,598 @@ +package com.volmit.iris.map; + +import com.volmit.iris.Iris; +import com.volmit.iris.generator.IrisComplex; +import com.volmit.iris.util.J; +import com.volmit.iris.util.KMap; +import com.volmit.iris.util.PrecisionStopwatch; +import com.volmit.iris.util.RollingSequence; +import io.netty.util.internal.ConcurrentSet; +import org.jetbrains.annotations.NotNull; + +import javax.annotation.Nullable; +import javax.imageio.ImageIO; +import javax.swing.JFrame; +import javax.swing.JPanel; +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Point; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionListener; +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; + +public class MapVision extends JPanel { + + private int threadId = 0; + + private static final int TILE_SIZE = 128; //Tile size in pixels + private static final int TILE_REALITY = 512; //How many blocks a tile is + private static final int TILE_SIZE_R = 7; //The number of bits to shift to get the pixel side + private static final int TILE_REALITY_R = 9; //The number of bits to shift to get the real size + + private static final int DEF_WIDTH = 1440; + private static final int DEF_HEIGHT = 820; + + + private IrisComplex complex; + private RenderType currentType = RenderType.BIOME_LAND; + + private int mouseX; //The current mouse coords + private int mouseY; + private double draggedOffsetX; //The amount the mouse has dragged the map + private double draggedOffsetY; + private int centerTileX; //The center tile in the screen + private int centerTileY; + private int offsetX; //Offset to draw tiles to + private int offsetY; + private int lastTileWidth; + + private boolean dirty = true; //Whether to repaint textures + private double scale = 1; + private boolean realname = false; + + private KMap tiles = new KMap<>(); + + private Set visibleTiles = new ConcurrentSet<>(); //Tiles that are visible on screen + private Set halfDirtyTiles = new ConcurrentSet<>(); //Tiles that should be drawn next draw + + private short[][] spiral; //See #generateSpiral + + private final Color overlay = new Color(80, 80, 80); + private final Font overlayFont = new Font("Arial", Font.BOLD, 16); + + private RollingSequence roll = new RollingSequence(50); + + private boolean debug = false; + private int[] debugBorder = new int[] {-5, -3, 6, 4}; + + private boolean recalculating; + + // IrisComplex is the main class I need for a biome map. You can make one from an Engine object, + // which does need a FakeWorld object in it for the seed + public MapVision(IrisComplex worldComplex) + { + this.complex = worldComplex; + this.setBackground(Color.BLACK); + this.setVisible(true); + roll.put(1); + generateSpiral(64); + + addMouseWheelListener((mouseWheelEvent) -> { + double oldScale = this.scale; + this.scale = Math.min(4, Math.max(scale + scale * mouseWheelEvent.getWheelRotation() * 0.2, 1)); + double wx = getWidth() / 2; + double hy = getHeight() / 2; + double xScale = (mouseX - wx) / wx; + double yScale = (mouseY - hy) / hy; + + /*if (mouseWheelEvent.getWheelRotation() > 0) { //Only on zoom in, adjust the position to zoom into + this.draggedOffsetX += xScale * (wx) * (oldScale - scale); + this.draggedOffsetY += yScale * (hy) * (oldScale - scale); + }*/ + + dirty = true; + repaint(); + softRecalculate(); + }); + addMouseMotionListener(new MouseMotionListener() + { + @Override + public void mouseMoved(MouseEvent e) + { + Point cp = e.getPoint(); + mouseX = cp.x; + mouseY = cp.y; + } + + @Override + public void mouseDragged(MouseEvent e) + { + Point cp = e.getPoint(); + draggedOffsetX -= (mouseX - cp.x) / scale; + draggedOffsetY -= (mouseY - cp.y) / scale; + mouseX = cp.x; + mouseY = cp.y; + softRecalculate(); + dirty = true; + } + }); + recalculate(); //Setup + + } + + /** + * Open this GUI + */ + public void open() { + JFrame frame = new JFrame("Iris Map (" + complex.getData().getDataFolder().getName() + ")"); + frame.add(this); + frame.setSize(DEF_WIDTH, DEF_HEIGHT); + frame.setBackground(Color.BLACK); + frame.addComponentListener(new ComponentListener() { + + @Override + public void componentResized(ComponentEvent e) { + dirty = true; + softRecalculate(); + repaint(); + } + + @Override + public void componentMoved(ComponentEvent e) { + dirty = true; + repaint(); + } + + @Override + public void componentShown(ComponentEvent e) { } + + @Override + public void componentHidden(ComponentEvent e) { } + }); + frame.addKeyListener(new KeyListener() { + @Override + public void keyTyped(KeyEvent e) { } + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_SHIFT) + realname = true; + else if (e.getKeyCode() == KeyEvent.VK_ALT) debug = !debug; + else if (e.getKeyCode() == KeyEvent.VK_R) { + dirty = true; + repaint(); + } + } + + @Override + public void keyReleased(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_SHIFT) + realname = false; + } + }); + File file = Iris.getCached("Iris Icon", "https://raw.githubusercontent.com/VolmitSoftware/Iris/master/icon.png"); + + if(file != null) { + try { + frame.setIconImage(ImageIO.read(file)); + } catch(IOException ignored) { } + } + + frame.setVisible(true); + frame.requestFocus(); + frame.toFront(); + frame.addFocusListener(new FocusListener() { + + @Override + public void focusGained(FocusEvent e) { + dirty = true; + } + + @Override + public void focusLost(FocusEvent e) { + + } + }); + } + + @Override + public void paint(Graphics gx) { + //super.paint(gx); + PrecisionStopwatch stopwatch = PrecisionStopwatch.start(); + + int windowOffsetX = getWidth() / 2; + int windowOffsetY = getHeight() / 2; + + offsetX = (int) Math.round(draggedOffsetX * scale) + windowOffsetX; + offsetY = (int) Math.round(draggedOffsetY * scale) + windowOffsetY; + + //If we should do a full repaint of the entire frame. Only done when the zoom level changes, etc + if (dirty) { + super.paint(gx); //Clear the frame first + for (Iterator iterator = visibleTiles.iterator(); iterator.hasNext();) { + Tile tile = iterator.next(); + drawTile(gx, tile); + } + dirty = false; + } else { + //Loop through all the tiles that haven't been drawn last draw and draw them + //This saves us having to do a FULL redraw when only 1 new tile has been added + for (Iterator iterator = halfDirtyTiles.iterator(); iterator.hasNext();) { + Tile tile = iterator.next(); + drawTile(gx, tile); + iterator.remove(); + } + } + + gx.setColor(overlay); + gx.fillRect(getWidth() - 400, 4, 396, 27); + gx.setColor(Color.WHITE); + //int x = (int) (((int) ((mouseX - windowOffsetX)) << 2) + (draggedOffsetX * scale)); + //int y = (int) (((int) ((mouseY - windowOffsetY)) << 2) + (draggedOffsetY * scale)); + int x = (int) (((int) ((mouseX - windowOffsetX))) - (draggedOffsetX)) << 2; + int y = (int) (((int) ((mouseY - windowOffsetY))) - (draggedOffsetY)) << 2; + String text = " [" + x+ ", " + y + "]"; + if (realname) + text = complex.getLandBiomeStream().get(x, y).getLoadKey().toUpperCase() + text; + else + text = complex.getLandBiomeStream().get(x, y).getName().toUpperCase() + text; + gx.setFont(overlayFont); + gx.drawString(text, getWidth() - 400 + 6, 23); + + if (debug) { + gx.setColor(Color.RED); + int xx = (int) Math.round((debugBorder[0] << TILE_SIZE_R) / scale + offsetX); + int yy = (int) Math.round((debugBorder[1] << TILE_SIZE_R) / scale + offsetY); + int xx2 = (int) Math.round((debugBorder[2] << TILE_SIZE_R) / scale + offsetX); + int yy2 = (int) Math.round((debugBorder[3] << TILE_SIZE_R) / scale + offsetY); + gx.drawRect(xx, yy, xx2, yy2); + gx.drawRect(xx-1, yy-1, xx2+1, yy2+1); + gx.drawRect(xx-2, yy-2, xx2+2, yy2+2); + + + gx.setColor(overlay); + gx.fillRect(10, 10, 220, 200); + gx.setColor(Color.WHITE); + gx.drawString("Center [" + centerTileX + ", " + centerTileY + "]", 20, 25); + gx.drawString((60 / (Math.max(roll.getAverage(), 1))) + " fps", 20, 45); + gx.drawString("Width = " + lastTileWidth, 20, 65); + gx.drawString("Dirty = " + dirty, 20, 85); + gx.drawString("Scale = " + scale, 20, 105); + gx.drawString("Tiles (Visible)" + visibleTiles.size(), 20, 125); + gx.drawString("Tiles (Total) " + tiles.size(), 20, 145); + + x = (int) (((int) ((mouseX - windowOffsetX))) + (-draggedOffsetX * scale)) >> TILE_SIZE_R; + y = (int) (((int) ((mouseY - windowOffsetY))) + (-draggedOffsetY * scale)) >> TILE_SIZE_R; + Tile t = getTile((short)x, (short)y); + boolean b1 = t != null; + boolean b2 = b1 && visibleTiles.contains(t); + gx.drawString("Cursor Tile [" + x + ", " + y + "]", 20, 165); + gx.drawString("Tile Details [" + String.valueOf(b1).toUpperCase() + ", " + String.valueOf(b2).toUpperCase() + "]", 20, 185); + + } + + stopwatch.end(); + roll.put(stopwatch.getMillis()); + + /*J.a(() -> + { + J.sleep(1000 / targetFPS); + repaint(); + });*/ + J.a(sleepTask); + } + + public void drawTile(Graphics gx, Tile tile) { + if (gx == null) return; + + int x = (int) Math.floor((tile.getX() << TILE_SIZE_R) / scale + offsetX); + int y = (int) Math.floor((tile.getY() << TILE_SIZE_R) / scale + offsetY); + + int size = (int) (TILE_SIZE / scale); + int off = (int) Math.round((TILE_SIZE % scale)); + gx.drawImage(tile.getImage(), x, y, size + off, size + off,null); + } + + private Runnable sleepTask = new Runnable() { + @Override + public void run() { + double t = Math.max(Math.min(roll.getAverage(), 1000), 30); + J.sleep((long) t); + repaint(); + } + }; + + /** + * Check if we should do a full recalculation of what tiles should be visible + */ + public void softRecalculate() { + short x = (short) (((-draggedOffsetX * scale)) / TILE_SIZE * scale); + short y = (short) (((-draggedOffsetY * scale)) / TILE_SIZE * scale); + int xTiles = (((int)(getWidth() * scale) >> TILE_SIZE_R)) / 2 + 1; + + if (centerTileX != x || centerTileY != y || xTiles != lastTileWidth) { + recalculate(); + } + + centerTileX = x; + centerTileY = y; + } + + /** + * Recalculate what tiles should be visible on screen, as well as queue + * new tiles to be created + */ + public void recalculate() { + PrecisionStopwatch stopwatch = PrecisionStopwatch.start(); + + //Clears out the queue of existing tiles to do because we are redoing them anyway + //If we don't do this, the queue gets so clogged that it literally takes up the + //entire CPU with thread locking/unlocking + executorService.getQueue().clear(); + + int W = getWidth(); + int H = getHeight(); + + if (W == 0|| H == 0) { //The window hasn't fully opened yet; assume defaults + W = DEF_WIDTH; + H = DEF_HEIGHT; + } + + short centerTileX = (short) (((-draggedOffsetX * scale)) / TILE_SIZE * scale); + short centerTileY = (short) (((-draggedOffsetY * scale)) / TILE_SIZE * scale); + + //Iris.info("Center is " + centerTileX + ", " + centerTileY); + //Iris.info("Width is " + W + ", " + H); + + int woh = Math.max(W, H); + int newSize = ((int)(woh * scale) >> TILE_SIZE_R) + 1; + int checkSizeX = (((int)(W * scale) >> TILE_SIZE_R)) / 2; + int checkSizeY = (((int)(H * scale) >> TILE_SIZE_R)) / 2; + lastTileWidth = checkSizeX; + generateSpiral(newSize); + + Set toRemove = new HashSet(visibleTiles); //Clone the visible tiles + + if (debug) { //These are the 4 corners of the red line that shows the visibility check region for tiles + debugBorder[0] = -checkSizeX + centerTileX - 1; + debugBorder[1] = -checkSizeY + centerTileY - 1; + debugBorder[2] = checkSizeX + centerTileX; + debugBorder[3] = checkSizeY + centerTileY; + } + + for (short[] coords : spiral) { //Start from the center of the spiral and work outwards to find new tiles to queue + short x = (short)(coords[0] + centerTileX); + short y = (short)(coords[1] + centerTileY); + + //When it goes offscreen, don't queue the tile by continuing + if (x > checkSizeX + centerTileX || x < -checkSizeX + centerTileX - 1) { + continue; + } + if (y > checkSizeY + centerTileY || y < -checkSizeY + centerTileY - 1) { + continue; + } + + int id = getTileId(x, y); + + //If the tile is not already made + if (!tiles.containsKey(id)) { + short[] c = getTileCoords(id); + queue(c[0], c[1]); //Queue for creation + } else { + Tile t = tiles.get(id); + toRemove.remove(t); //Make sure this tile isn't removed + + if (!visibleTiles.contains(t)) { + visibleTiles.add(t); //Make sure it's visible again if it isn't + } + } + } + + queueForRemoval(toRemove); //Queue all tiles not on screen for removal + + stopwatch.end(); + roll.put(stopwatch.getMillis()); + } + + /** + * Queue a tile for creation + * @param tileX X tile coord + * @param tileY Y tile coord + */ + public void queue(short tileX, short tileY) { + //If the tile still exists but just isn't visible + int id = getTileId(tileX, tileY); + if (tiles.containsKey(id)) { + Tile tile = tiles.get(id); + if (visibleTiles.contains(tile)) return; + + visibleTiles.add(tile); + halfDirtyTiles.add(tile); //Re-render it without doing a full repaint + //dirty = true; + return; + } + + //I turned all lambda around here into objects just to see if they would + //show up in timings instead of "$lambda". But they didn't. So it's not + //not my code DIRECTLY. I believe the thing timings show is just to do + //with threads stopping and starting/halting in the thread pool. Don't + //know why or how to fix it, though + + /*executorService.execute(() -> { + Tile tile = new Tile(tileX, tileY); + tile.render(complex, currentType); + tiles.put(getTileId(tileX, tileY), tile); + visibleTiles.add(tile); + dirty = true; + });*/ + executorService.execute(queueTask(tileX, tileY)); + + } + + public Runnable queueTask(short tileX, short tileY) { + return new Runnable() { + @Override + public void run() { + Tile tile = new Tile(tileX, tileY); + tile.render(complex, currentType); + tiles.put(getTileId(tileX, tileY), tile); + visibleTiles.add(tile); + //dirty = true; //Disabled marking as dirty so a redraw of the entire map isn't needed + halfDirtyTiles.add(tile); + } + }; + } + + /** + * Pend tiles for removal from the screen + */ + public void queueForRemoval(Collection tile) { + J.a(() -> { + for (Tile t : tile) { + if (t != null) { + visibleTiles.remove(t); + } + } + }, 20); + + //TODO Change from using the async task system as it may be putting strain on the server from being called so often + + J.a(() -> { //Remove it completely from memory after 5 seconds if it's still not visible + for (Tile t : tile) { + if (t != null && !visibleTiles.contains(t)) { + tiles.remove(t); + } + } + + }, 20 * 6); + } + + /** + * Get a tile based on the X and Z coords of the tile + * @param tileX X Coord + * @param tileY Y Coord + * @return + */ + @Nullable + public Tile getTile(short tileX, short tileY) { + return tiles.get(getTileId(tileX, tileY)); + } + + /** + * Get an integer that represents a tile's location + * @param tileX X Coord + * @param tileY Y Coord + * @return + */ + public int getTileId(short tileX, short tileY) { + return (tileX << 16) | (tileY & 0xFFFF); + } + + /** + * Converts an integer representing a tiles location back into 2 shorts + * @param id The tile integer + * @return + */ + public short[] getTileCoords(int id) { + return new short[] {(short)(id >> 16), (short) id}; + } + + /** + * Generates a 2D array of relative tile locations. This is so we know what order + * to search for new tiles in a nice, spiral way + * @param size Size of the array + */ + public void generateSpiral(int size) { + if (size % 2 == 0) size++; + short[][] newSpiral = new short[size * size][2]; + + int x = 0; // current position; x + int y = 0; // current position; y + int d = 0; // current direction; 0=RIGHT, 1=DOWN, 2=LEFT, 3=UP + int s = 1; // chain size + int c = 0; // count + + // starting point + x = ((int)(size/2.0))-1; + y = ((int)(size/2.0))-1; + int offset = (size / 2) - 1; + + for (int k=1; k<=(size-1); k++) + { + for (int j=0; j<(k<(size-1)?2:3); j++) + { + for (int i=0; i { + threadId++; + Thread t = new Thread(r); + t.setName("Iris Map Renderer " + threadId); + t.setPriority(Thread.MIN_PRIORITY); + t.setUncaughtExceptionHandler((et, e) -> + { + Iris.info("Exception encountered in " + et.getName()); + e.printStackTrace(); + }); + + return t; + });*/ + + private ThreadFactory factory = new ThreadFactory() { + @Override + public Thread newThread(@NotNull Runnable r) { + threadId++; + Thread t = new Thread(r); + t.setName("Iris Map Renderer " + threadId); + t.setPriority(4); + t.setDaemon(true); + t.setUncaughtExceptionHandler((et, e) -> + { + Iris.info("Exception encountered in " + et.getName()); + e.printStackTrace(); + }); + + return t; + } + }; + + //Our thread pool that draws the tiles for us + private final ThreadPoolExecutor executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(3, factory); + + +} diff --git a/src/main/java/com/volmit/iris/map/RenderType.java b/src/main/java/com/volmit/iris/map/RenderType.java new file mode 100644 index 000000000..3193eb49f --- /dev/null +++ b/src/main/java/com/volmit/iris/map/RenderType.java @@ -0,0 +1,5 @@ +package com.volmit.iris.map; + +public enum RenderType { + BIOME_LAND, REGION, CAVE_LAND, HEIGHT +} diff --git a/src/main/java/com/volmit/iris/map/Tile.java b/src/main/java/com/volmit/iris/map/Tile.java new file mode 100644 index 000000000..428c08d2a --- /dev/null +++ b/src/main/java/com/volmit/iris/map/Tile.java @@ -0,0 +1,86 @@ +package com.volmit.iris.map; + +import com.volmit.iris.generator.IrisComplex; +import com.volmit.iris.object.IrisBiome; +import com.volmit.iris.object.IrisRegion; +import com.volmit.iris.scaffold.stream.ProceduralStream; +import lombok.Getter; +import lombok.Setter; + +import java.awt.Color; +import java.awt.image.BufferedImage; +import java.util.Set; +import java.util.function.BiFunction; + +public class Tile { + + @Getter + private short x; + @Getter + private short y; + + @Getter + private BufferedImage image; + + private Set biomes; + private Set regions; + + + + @Getter + @Setter + private boolean dirty; + + @Getter + private boolean rendering; + + public Tile(short x, short y) { + this.x = x; + this.y = y; + } + + public boolean hasBiome(int biome) { + return biomes.contains(biome); + } + + public boolean hasRegion(int region) { + return regions.contains(region); + } + + /** + * Render the tile + * @param complex The world complex + * @param type The type of render + * @return True when rendered + */ + public boolean render(IrisComplex complex, RenderType type) { + BufferedImage newImage = new BufferedImage(128, 128, BufferedImage.TYPE_INT_RGB); + + BiFunction getColor; + if (type == RenderType.BIOME_LAND) { + getColor = (x, z) -> complex.getLandBiomeStream().get(x, z).getColor().getRGB(); + } else if (type == RenderType.REGION) { + getColor = (x, z) -> complex.getRegionStream().get(x, z).getColor(complex).getRGB(); + } else if (type == RenderType.HEIGHT) { + getColor = (x, z) -> Color.getHSBColor(complex.getHeightStream().get(x, z).floatValue(), 100, 100).getRGB(); + } else { + getColor = (x, z) -> complex.getCaveBiomeStream().get(x, z).getColor().getRGB(); + } + + for (int i = 0; i < 128; i++) { + for (int j = 0; j < 128; j++) { + newImage.setRGB(i, j, getColor.apply(translate(x, i), translate(y, j))); + } + } + image = newImage; + rendering = false; + dirty = false; + return true; + } + + public static int translate(int section, int pixel) { + return (section << 9) | (pixel << 2) | 2; + } + + +} diff --git a/src/main/java/com/volmit/iris/object/IrisBiome.java b/src/main/java/com/volmit/iris/object/IrisBiome.java index 36d2c6605..40d1bd26c 100644 --- a/src/main/java/com/volmit/iris/object/IrisBiome.java +++ b/src/main/java/com/volmit/iris/object/IrisBiome.java @@ -109,7 +109,7 @@ public class IrisBiome extends IrisRegistrant implements IRare { @Desc("A color for visualizing this biome with a color. I.e. #F13AF5. This will show up on the map.") - private IrisColor color = null; + private String color = null; @Required @@ -194,7 +194,7 @@ public class IrisBiome extends IrisRegistrant implements IRare { private final transient AtomicCache> genCacheMin = new AtomicCache<>(); private final transient AtomicCache> surfaceObjectsCache = new AtomicCache<>(false); private final transient AtomicCache> carveObjectsCache = new AtomicCache<>(false); - private final transient AtomicCache cacheColor = new AtomicCache<>(true); + private final transient AtomicCache cacheColor = new AtomicCache<>(); private final transient AtomicCache childrenCell = new AtomicCache<>(); private final transient AtomicCache biomeGenerator = new AtomicCache<>(); private final transient AtomicCache maxHeight = new AtomicCache<>(); @@ -598,4 +598,29 @@ public class IrisBiome extends IrisRegistrant implements IRare { return getLayers().get(0).get(rng, x, 0, z, idm); } + + public Color getColor() { + return this.cacheColor.aquire(() -> { + if (this.color == null) { + RandomColor randomColor = new RandomColor(getName().hashCode()); + if (this.getVanillaDerivative() == null) { + Iris.warn("No vanilla biome found for " + getName()); + return new Color(randomColor.randomColor()); + } + RandomColor.Color col = VanillaBiomeMap.getColorType(this.getVanillaDerivative()); + RandomColor.Luminosity lum = VanillaBiomeMap.getColorLuminosity(this.getVanillaDerivative()); + RandomColor.SaturationType sat = VanillaBiomeMap.getColorSaturatiom(this.getVanillaDerivative()); + int newColorI = randomColor.randomColor(col, col == RandomColor.Color.MONOCHROME ? RandomColor.SaturationType.MONOCHROME : sat, lum); + + return new Color(newColorI); + } + + try { + return Color.decode(this.color); + } catch (NumberFormatException e) { + Iris.warn("Could not parse color \"" + this.color + "\" for biome " + getName()); + return new Color(new RandomColor(getName().hashCode()).randomColor()); + } + }); + } } diff --git a/src/main/java/com/volmit/iris/object/IrisColor.java b/src/main/java/com/volmit/iris/object/IrisColor.java index 60675eb7c..28aa788d0 100644 --- a/src/main/java/com/volmit/iris/object/IrisColor.java +++ b/src/main/java/com/volmit/iris/object/IrisColor.java @@ -29,6 +29,7 @@ import lombok.experimental.Accessors; import java.awt.*; +@Deprecated(forRemoval = true) @Accessors(chain = true) @NoArgsConstructor @Desc("Represents a color") @@ -105,4 +106,17 @@ public class IrisColor { return new Color(a << 24 | r << 16 | g << 8 | b); } + + public int getAsRGB() { + if (hex != null) { + try { + if (hex.startsWith("#")) hex = hex.substring(1); + return Integer.parseInt(hex, 16); + } catch (NumberFormatException e) { + return 0; + } + } + + return red << 16 | green << 8 | blue; + } } diff --git a/src/main/java/com/volmit/iris/object/IrisRegion.java b/src/main/java/com/volmit/iris/object/IrisRegion.java index e4d9e3ce8..fa50f6c6b 100644 --- a/src/main/java/com/volmit/iris/object/IrisRegion.java +++ b/src/main/java/com/volmit/iris/object/IrisRegion.java @@ -18,6 +18,7 @@ package com.volmit.iris.object; +import com.volmit.iris.Iris; import com.volmit.iris.generator.noise.CNG; import com.volmit.iris.manager.IrisDataManager; import com.volmit.iris.scaffold.cache.AtomicCache; @@ -28,6 +29,9 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; +import java.awt.Color; +import java.util.Random; + @SuppressWarnings("DefaultAnnotationParam") @Accessors(chain = true) @@ -209,7 +213,7 @@ public class IrisRegion extends IrisRegistrant implements IRare { @Desc("A color for visualizing this region with a color. I.e. #F13AF5. This will show up on the map.") - private IrisColor color = null; + private String color = null; private final transient AtomicCache> surfaceObjectsCache = new AtomicCache<>(); private final transient AtomicCache> carveObjectsCache = new AtomicCache<>(); @@ -225,6 +229,7 @@ public class IrisRegion extends IrisRegistrant implements IRare { private final transient AtomicCache lakeGen = new AtomicCache<>(); private final transient AtomicCache riverGen = new AtomicCache<>(); private final transient AtomicCache riverChanceGen = new AtomicCache<>(); + private final transient AtomicCache cacheColor = new AtomicCache<>(); public String getName() { return name; @@ -508,4 +513,45 @@ public class IrisRegion extends IrisRegistrant implements IRare { return b.v(); } + + public Color getColor(DataProvider dataProvider) { + + return this.cacheColor.aquire(() -> { + if (this.color == null) { + Random rand = new Random(getName().hashCode() + getAllBiomeIds().hashCode()); + RandomColor randomColor = new RandomColor(rand); + + KList biomes = getRealLandBiomes(dataProvider); + + while (biomes.size() > 0) { + int index = rand.nextInt(biomes.size()); + IrisBiome biome = biomes.get(index); + + if (biome.getVanillaDerivative() != null) { + RandomColor.Color col = VanillaBiomeMap.getColorType(biome.getVanillaDerivative()); + RandomColor.Luminosity lum = VanillaBiomeMap.getColorLuminosity(biome.getVanillaDerivative()); + RandomColor.SaturationType sat = VanillaBiomeMap.getColorSaturatiom(biome.getVanillaDerivative()); + int newColorI = randomColor.randomColor(col, col == RandomColor.Color.MONOCHROME ? RandomColor.SaturationType.MONOCHROME : sat, lum); + return new Color(newColorI); + } + + biomes.remove(index); + } + + Iris.warn("Couldn't find a suitable color for region " + getName()); + return new Color(new RandomColor(rand).randomColor()); + } + + try { + return Color.decode(this.color); + } catch (NumberFormatException e) { + Iris.warn("Could not parse color \"" + this.color + "\" for region " + getName()); + return Color.WHITE; + } + }); + } + + public void pickRandomColor(DataProvider data) { + + } } diff --git a/src/main/java/com/volmit/iris/scaffold/engine/Engine.java b/src/main/java/com/volmit/iris/scaffold/engine/Engine.java index 169e3fdd9..d09b017e1 100644 --- a/src/main/java/com/volmit/iris/scaffold/engine/Engine.java +++ b/src/main/java/com/volmit/iris/scaffold/engine/Engine.java @@ -110,10 +110,11 @@ public interface Engine extends DataProvider, Fallible, GeneratorAccess, LootPro IrisBiome biome = getSurfaceBiome((int) x, (int) z); int height = getHeight((int) x, (int) z); double heightFactor = M.lerpInverse(0, getHeight(), height); - IrisColor irc = region.getColor(); - IrisColor ibc = biome.getColor(); - Color rc = irc != null ? irc.getColor() : Color.GREEN.darker(); - Color bc = ibc != null ? ibc.getColor() : biome.isAquatic() ? Color.BLUE : Color.YELLOW; + Color irc = region.getColor(this.getFramework().getComplex()); + Color ibc = biome.getColor(); + Color rc = irc != null ? irc : Color.GREEN.darker(); + Color bc = ibc != null ? ibc : biome.isAquatic() ? Color.BLUE : Color.YELLOW; + Color f = IrisColor.blend(rc, bc, bc, Color.getHSBColor(0, 0, (float) heightFactor)); return IrisColor.blend(rc, bc, bc, Color.getHSBColor(0, 0, (float) heightFactor)); } diff --git a/src/main/java/com/volmit/iris/util/FakeEngine.java b/src/main/java/com/volmit/iris/util/FakeEngine.java new file mode 100644 index 000000000..66654b736 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/FakeEngine.java @@ -0,0 +1,139 @@ +package com.volmit.iris.util; + +import com.volmit.iris.manager.IrisDataManager; +import com.volmit.iris.object.IrisBiome; +import com.volmit.iris.object.IrisDimension; +import com.volmit.iris.scaffold.engine.Engine; +import com.volmit.iris.scaffold.engine.EngineCompound; +import com.volmit.iris.scaffold.engine.EngineEffects; +import com.volmit.iris.scaffold.engine.EngineFramework; +import com.volmit.iris.scaffold.engine.EngineMetrics; +import com.volmit.iris.scaffold.engine.EngineTarget; +import com.volmit.iris.scaffold.engine.EngineWorldManager; +import com.volmit.iris.scaffold.hunk.Hunk; +import com.volmit.iris.util.FakeWorld; +import lombok.Getter; +import org.bukkit.World; +import org.bukkit.block.Biome; +import org.bukkit.block.data.BlockData; + + +public class FakeEngine implements Engine { + + @Getter + private IrisDimension dimension; + + @Getter + private World world; + + public FakeEngine(IrisDimension dimension, FakeWorld world) { + this.dimension = dimension; + this.world = world; + } + + @Override + public void close() { } + + @Override + public boolean isClosed() { + return false; + } + + @Override + public IrisDataManager getData() { + return dimension.getLoader().copy(); + } + + @Override + public EngineWorldManager getWorldManager() { + return null; + } + + @Override + public void setParallelism(int parallelism) { } + + @Override + public int getParallelism() { + return 0; + } + + @Override + public EngineTarget getTarget() { + return null; + } + + @Override + public EngineFramework getFramework() { + return null; + } + + @Override + public void setMinHeight(int min) { } + + @Override + public void recycle() { } + + @Override + public int getIndex() { + return 0; + } + + @Override + public int getMinHeight() { + return 0; + } + + @Override + public int getHeight() { + return 64; + } + + @Override + public double modifyX(double x) { + return 0; + } + + @Override + public double modifyZ(double z) { + return 0; + } + + @Override + public void generate(int x, int z, Hunk blocks, Hunk biomes) { } + + @Override + public EngineMetrics getMetrics() { + return null; + } + + @Override + public EngineEffects getEffects() { + return null; + } + + @Override + public EngineCompound getCompound() { + return null; + } + + @Override + public IrisBiome getFocus() { + return null; + } + + @Override + public void fail(String error, Throwable e) { } + + @Override + public boolean hasFailed() { + return false; + } + + @Override + public int getCacheID() { + return 0; + } + + @Override + public void hotload() { } +} diff --git a/src/main/java/com/volmit/iris/util/RandomColor.java b/src/main/java/com/volmit/iris/util/RandomColor.java new file mode 100644 index 000000000..be0ec0b78 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/RandomColor.java @@ -0,0 +1,511 @@ +package com.volmit.iris.util; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Random; + +/** + * Credit to https://github.com/lzyzsd/AndroidRandomColor + */ +public class RandomColor { + + public static int hueOffset = 0; + + public static class ColorInfo { + Range hueRange; + Range saturationRange; + Range brightnessRange; + List lowerBounds; + + public ColorInfo(Range hueRange, Range saturationRange, Range brightnessRange, List lowerBounds) { + this.hueRange = hueRange; + this.saturationRange = saturationRange; + this.brightnessRange = brightnessRange; + this.lowerBounds = lowerBounds; + } + + public Range getHueRange() { + return hueRange; + } + + public void setHueRange(Range hueRange) { + this.hueRange = hueRange; + } + + public Range getSaturationRange() { + return saturationRange; + } + + public void setSaturationRange(Range saturationRange) { + this.saturationRange = saturationRange; + } + + public Range getBrightnessRange() { + return brightnessRange; + } + + public void setBrightnessRange(Range brightnessRange) { + this.brightnessRange = brightnessRange; + } + + public List getLowerBounds() { + return lowerBounds; + } + + public void setLowerBounds(List lowerBounds) { + this.lowerBounds = lowerBounds; + } + } + + public static class Range { + int start; + int end; + + public Range(int start, int end) { + this.start = start; + this.end = end; + } + + public boolean contain(int value) { + return value >= start && value <= end; + } + + @Override + public String toString() { + return "start: " + start + " end: " + end; + } + } + + private Random random; + + public static enum SaturationType { + RANDOM, MONOCHROME, HIGH, LOW, MEDIUM + } + + public static enum Luminosity { + BRIGHT, LIGHT, DARK, RANDOM + } + + public static class Options { + int hue; + SaturationType saturationType; + Luminosity luminosity; + + public int getHue() { + return hue; + } + + public void setHue(int hue) { + this.hue = hue; + } + + public SaturationType getSaturationType() { + return saturationType; + } + + public void setSaturationType(SaturationType saturationType) { + this.saturationType = saturationType; + } + + public Luminosity getLuminosity() { + return luminosity; + } + + public void setLuminosity(Luminosity luminosity) { + this.luminosity = luminosity; + } + } + + private HashMap colors = new HashMap<>(); + + public RandomColor() { + loadColorBounds(); + random = new Random(); + } + + public RandomColor(long seed){ + loadColorBounds(); + random = new Random(); + random.setSeed(seed); + } + + public RandomColor(Random random) { + loadColorBounds(); + this.random = random; + } + + private int getColor(int hue, int saturation, int brightness) { + return java.awt.Color.getHSBColor((float)(hue + hueOffset % 360) / 360, (float)saturation / 100, (float)brightness / 100).getRGB(); + } + + public int randomColor() { + return randomColor(0, null, null); + } + + public int randomColor(int value, SaturationType saturationType, Luminosity luminosity) { + int hue = value; + hue = pickHue(hue); + int saturation = pickSaturation(hue, saturationType, luminosity); + int brightness = pickBrightness(hue, saturation, luminosity); + + return getColor(hue, saturation, brightness); + } + + public int randomColor(Color color, SaturationType saturationType, Luminosity luminosity) { + int hue = pickHue(color.name()); + int saturation = pickSaturation(hue, saturationType, luminosity); + int brightness = pickBrightness(hue, saturation, luminosity); + + return getColor(hue, saturation, brightness); + } + + public int[] randomColor(int count) { + if (count <= 0) { + throw new IllegalArgumentException("count must be greater than 0"); + } + + int[] colors = new int[count]; + for (int i = 0; i < count; i++) { + colors[i] = randomColor(); + } + + return colors; + } + + public int randomColor(Color color) { + int hue = pickHue(color.name()); + int saturation = pickSaturation(color, null, null); + int brightness = pickBrightness(color, saturation, null); + + int colorValue = getColor(hue, saturation, brightness); + return colorValue; + } + + public int[] random(Color color, int count) { + if (count <= 0) { + throw new IllegalArgumentException("count must be greater than 0"); + } + + int[] colors = new int[count]; + for (int i = 0; i < count; i++) { + colors[i] = randomColor(color); + } + + return colors; + } + + private int pickHue(int hue) { + Range hueRange = getHueRange(hue); + return doPickHue(hueRange); + } + + private int doPickHue(Range hueRange) { + int hue = randomWithin(hueRange); + + // Instead of storing red as two seperate ranges, + // we group them, using negative numbers + if (hue < 0) { + hue = 360 + hue; + } + + return hue; + } + + private int pickHue(String name) { + Range hueRange = getHueRange(name); + return doPickHue(hueRange); + } + + private Range getHueRange(int number) { + if (number < 360 && number > 0) { + return new Range(number, number); + } + + return new Range(0, 360); + } + + private Range getHueRange(String name) { + if (colors.containsKey(name)) { + return colors.get(name).getHueRange(); + } + + return new Range(0, 360); + } + + private int pickSaturation(int hue, SaturationType saturationType, Luminosity luminosity) { + return pickSaturation(getColorInfo(hue), saturationType, luminosity); + } + + private int pickSaturation(Color color, SaturationType saturationType, Luminosity luminosity) { + ColorInfo colorInfo = colors.get(color.name()); + return pickSaturation(colorInfo, saturationType, luminosity); + } + + private int pickSaturation(ColorInfo colorInfo, SaturationType saturationType, Luminosity luminosity) { + if (saturationType != null) { + switch (saturationType) { + case RANDOM: + return randomWithin(new Range(0, 100)); + case MONOCHROME: + return 0; + case HIGH: + return randomWithin(new Range(75, 100)); + case MEDIUM: + return randomWithin(new Range(55, 75)); + case LOW: + return randomWithin(new Range(35, 55)); + } + } + + if (colorInfo == null) { + return 0; + } + + Range saturationRange = colorInfo.getSaturationRange(); + + int min = saturationRange.start; + int max = saturationRange.end; + + if (luminosity != null) { + switch (luminosity) { + case LIGHT: + min = 55; + break; + case BRIGHT: + min = max - 10; + break; + case DARK: + max = 55; + break; + } + } + + return randomWithin(new Range(min, max)); + } + + private int pickBrightness(int hue, int saturation, Luminosity luminosity) { + ColorInfo colorInfo = getColorInfo(hue); + + return pickBrightness(colorInfo, saturation, luminosity); + } + + private int pickBrightness(Color color, int saturation, Luminosity luminosity) { + ColorInfo colorInfo = colors.get(color.name()); + + return pickBrightness(colorInfo, saturation, luminosity); + } + + private int pickBrightness(ColorInfo colorInfo, int saturation, Luminosity luminosity) { + int min = getMinimumBrightness(colorInfo, saturation), + max = 100; + + if (luminosity != null) { + switch (luminosity) { + + case DARK: + max = min + 20; + break; + + case LIGHT: + min = (max + min) / 2; + break; + + case RANDOM: + min = 0; + max = 100; + break; + } + } + + return randomWithin(new Range(min, max)); + } + + private int getMinimumBrightness(ColorInfo colorInfo, int saturation) { + if (colorInfo == null) { + return 0; + } + + List lowerBounds = colorInfo.getLowerBounds(); + for (int i = 0; i < lowerBounds.size() - 1; i++) { + + int s1 = lowerBounds.get(i).start, + v1 = lowerBounds.get(i).end; + + if (i == lowerBounds.size() - 1) { + break; + } + int s2 = lowerBounds.get(i + 1).start, + v2 = lowerBounds.get(i + 1).end; + + if (saturation >= s1 && saturation <= s2) { + + float m = (v2 - v1)/(float) (s2 - s1), + b = v1 - m*s1; + + return (int) (m*saturation + b); + } + + } + + return 0; + } + + private ColorInfo getColorInfo(int hue) { + // Maps red colors to make picking hue easier + if (hue >= 334 && hue <= 360) { + hue-= 360; + } + + for(String key : colors.keySet()) { + ColorInfo colorInfo = colors.get(key); + if (colorInfo.getHueRange() != null && colorInfo.getHueRange().contain(hue)) { + return colorInfo; + } + } + + return null; + } + + private int randomWithin (Range range) { + return (int) Math.floor(range.start + random.nextDouble()*(range.end + 1 - range.start)); + } + + public void defineColor(String name, Range hueRange, List lowerBounds) { + int sMin = lowerBounds.get(0).start; + int sMax = lowerBounds.get(lowerBounds.size() - 1).start; + int bMin = lowerBounds.get(lowerBounds.size() - 1).end; + int bMax = lowerBounds.get(0).end; + + colors.put(name, new ColorInfo(hueRange, new Range(sMin, sMax), new Range(bMin, bMax), lowerBounds)); + } + + private void loadColorBounds() { + List lowerBounds1 = new ArrayList<>(); + lowerBounds1.add(new Range(0, 0)); + lowerBounds1.add(new Range(100, 0)); + defineColor( + Color.MONOCHROME.name(), + new Range(0, 0), + lowerBounds1 + ); + + List lowerBounds2 = new ArrayList<>(); + lowerBounds2.add(new Range(20, 100)); + lowerBounds2.add(new Range(30, 92)); + lowerBounds2.add(new Range(40, 89)); + lowerBounds2.add(new Range(50, 85)); + lowerBounds2.add(new Range(60, 78)); + lowerBounds2.add(new Range(70, 70)); + lowerBounds2.add(new Range(80, 60)); + lowerBounds2.add(new Range(90, 55)); + lowerBounds2.add(new Range(100, 50)); + defineColor( + Color.RED.name(), + new Range(-26, 18), + lowerBounds2 + ); + + List lowerBounds3 = new ArrayList(); + lowerBounds3.add(new Range(20, 100)); + lowerBounds3.add(new Range(30, 93)); + lowerBounds3.add(new Range(40, 88)); + lowerBounds3.add(new Range(50, 86)); + lowerBounds3.add(new Range(60, 85)); + lowerBounds3.add(new Range(70, 70)); + lowerBounds3.add(new Range(100, 70)); + defineColor( + Color.ORANGE.name(), + new Range(19, 46), + lowerBounds3 + ); + + List lowerBounds4 = new ArrayList<>(); + lowerBounds4.add(new Range(25, 100)); + lowerBounds4.add(new Range(40, 94)); + lowerBounds4.add(new Range(50, 89)); + lowerBounds4.add(new Range(60, 86)); + lowerBounds4.add(new Range(70, 84)); + lowerBounds4.add(new Range(80, 82)); + lowerBounds4.add(new Range(90, 80)); + lowerBounds4.add(new Range(100, 75)); + + defineColor( + Color.YELLOW.name(), + new Range(47, 62), + lowerBounds4 + ); + + List lowerBounds5 = new ArrayList<>(); + lowerBounds5.add(new Range(30, 100)); + lowerBounds5.add(new Range(40, 90)); + lowerBounds5.add(new Range(50, 85)); + lowerBounds5.add(new Range(60, 81)); + lowerBounds5.add(new Range(70, 74)); + lowerBounds5.add(new Range(80, 64)); + lowerBounds5.add(new Range(90, 50)); + lowerBounds5.add(new Range(100, 40)); + + defineColor( + Color.GREEN.name(), + new Range(63,178), + lowerBounds5 + ); + + List lowerBounds6 = new ArrayList<>(); + lowerBounds6.add(new Range(20, 100)); + lowerBounds6.add(new Range(30, 86)); + lowerBounds6.add(new Range(40, 80)); + lowerBounds6.add(new Range(50, 74)); + lowerBounds6.add(new Range(60, 60)); + lowerBounds6.add(new Range(70, 52)); + lowerBounds6.add(new Range(80, 44)); + lowerBounds6.add(new Range(90, 39)); + lowerBounds6.add(new Range(100, 35)); + + defineColor( + Color.BLUE.name(), + new Range(179, 257), + lowerBounds6 + ); + + List lowerBounds7 = new ArrayList<>(); + lowerBounds7.add(new Range(20, 100)); + lowerBounds7.add(new Range(30, 87)); + lowerBounds7.add(new Range(40, 79)); + lowerBounds7.add(new Range(50, 70)); + lowerBounds7.add(new Range(60, 65)); + lowerBounds7.add(new Range(70, 59)); + lowerBounds7.add(new Range(80, 52)); + lowerBounds7.add(new Range(90, 45)); + lowerBounds7.add(new Range(100, 42)); + + defineColor( + Color.PURPLE.name(), + new Range(258, 282), + lowerBounds7 + ); + + List lowerBounds8 = new ArrayList<>(); + lowerBounds8.add(new Range(20, 100)); + lowerBounds8.add(new Range(30, 90)); + lowerBounds8.add(new Range(40, 86)); + lowerBounds8.add(new Range(60, 84)); + lowerBounds8.add(new Range(80, 80)); + lowerBounds8.add(new Range(90, 75)); + lowerBounds8.add(new Range(100, 73)); + + defineColor( + Color.PINK.name(), + new Range(283, 334), + lowerBounds8 + ); + } + + public static enum Color { + MONOCHROME, RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE, PINK + } + +} + + diff --git a/src/main/java/com/volmit/iris/util/VanillaBiomeMap.java b/src/main/java/com/volmit/iris/util/VanillaBiomeMap.java new file mode 100644 index 000000000..ddb6fe099 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/VanillaBiomeMap.java @@ -0,0 +1,128 @@ +package com.volmit.iris.util; + +import org.apache.commons.lang3.tuple.ImmutableTriple; +import org.apache.commons.lang3.tuple.Triple; +import org.bukkit.block.Biome; +import com.volmit.iris.util.RandomColor.*; + +public class VanillaBiomeMap { + + private static KMap BIOME_HEX = new KMap<>(); + private static KMap BIOME_COLOR = new KMap<>(); + private static KMap BIOME_LUMINOSITY = new KMap<>(); + private static KMap BIOME_SATURATION = new KMap<>(); + private static KMap BIOME_IDs = new KMap<>(); + + private static void add(Biome biome, int color, short id, Color randomColor, Luminosity luminosity, SaturationType saturation) { + BIOME_HEX.put(biome, color); + BIOME_COLOR.put(biome, randomColor); + if (luminosity != null) BIOME_LUMINOSITY.put(biome, luminosity); + if (saturation != null) BIOME_SATURATION.put(biome, saturation); + BIOME_IDs.put(biome, id); + } + + private static void add(Biome biome, int color, short id, Color randomColor, Luminosity luminosity) { + add(biome, color, id, randomColor, luminosity, null); + } + + public static int getColor(Biome biome) { + return BIOME_HEX.get(biome); + } + + public static Color getColorType(Biome biome) { + return BIOME_COLOR.get(biome); + } + + public static Luminosity getColorLuminosity(Biome biome) { + return BIOME_LUMINOSITY.get(biome); + } + + public static SaturationType getColorSaturatiom(Biome biome) { + return BIOME_SATURATION.get(biome); + } + + public static short getId(Biome biome) { + return BIOME_IDs.get(biome); + } + + static { + add(Biome.OCEAN, 0x000070, (short) 0, Color.BLUE, Luminosity.BRIGHT, SaturationType.MEDIUM); + add(Biome.PLAINS, 0x8DB360, (short) 1, Color.GREEN, Luminosity.LIGHT, SaturationType.MEDIUM); + add(Biome.DESERT, 0xFA9418, (short) 2, Color.YELLOW, Luminosity.LIGHT, SaturationType.MEDIUM); + add(Biome.MOUNTAINS, 0x606060, (short) 3, Color.MONOCHROME, Luminosity.BRIGHT, null); + add(Biome.FOREST, 0x056621, (short) 4, Color.GREEN, Luminosity.BRIGHT); + add(Biome.TAIGA, 0x0B6659, (short) 5, Color.GREEN, Luminosity.BRIGHT, SaturationType.MEDIUM); + add(Biome.SWAMP, 0x07F9B2, (short) 6, Color.ORANGE, Luminosity.DARK, SaturationType.MEDIUM); + add(Biome.RIVER, 0x0000FF, (short) 7, Color.BLUE, Luminosity.LIGHT, SaturationType.LOW); + add(Biome.NETHER_WASTES, 0xBF3B3B, (short) 8, Color.RED, Luminosity.LIGHT, SaturationType.MEDIUM); + add(Biome.THE_END, 0x8080FF, (short) 9, Color.PURPLE, Luminosity.LIGHT, SaturationType.LOW); + add(Biome.FROZEN_OCEAN, 0x7070D6, (short) 10, Color.BLUE, Luminosity.BRIGHT, SaturationType.MEDIUM); + add(Biome.FROZEN_RIVER, 0xA0A0FF, (short) 11, Color.BLUE, Luminosity.BRIGHT, SaturationType.MEDIUM); + add(Biome.SNOWY_TUNDRA, 0xFFFFFF, (short) 12, Color.MONOCHROME, Luminosity.LIGHT); + add(Biome.SNOWY_MOUNTAINS, 0xA0A0A0, (short) 13, Color.MONOCHROME, Luminosity.LIGHT); + add(Biome.MUSHROOM_FIELDS, 0xFF00FF, (short) 14, Color.PURPLE, Luminosity.BRIGHT); + add(Biome.MUSHROOM_FIELD_SHORE, 0xA000FF, (short) 15, Color.PURPLE, Luminosity.BRIGHT); + add(Biome.BEACH, 0xFADE55, (short) 16, Color.YELLOW, Luminosity.LIGHT, SaturationType.LOW); + add(Biome.DESERT_HILLS, 0xD25F12, (short) 17, Color.YELLOW, Luminosity.LIGHT, SaturationType.MEDIUM); + add(Biome.WOODED_HILLS, 0x22551C, (short) 18, Color.GREEN, Luminosity.LIGHT); + add(Biome.TAIGA_HILLS, 0x163933, (short) 19, Color.GREEN, Luminosity.BRIGHT, SaturationType.MEDIUM); + add(Biome.MOUNTAIN_EDGE, 0x72789A, (short) 20, Color.MONOCHROME, Luminosity.BRIGHT); + add(Biome.JUNGLE, 0x537B09, (short) 21, Color.GREEN, Luminosity.BRIGHT, SaturationType.HIGH); + add(Biome.JUNGLE_HILLS, 0x2C4205, (short) 22, Color.GREEN, Luminosity.DARK, SaturationType.HIGH); + add(Biome.JUNGLE_EDGE, 0x628B17, (short) 23, Color.GREEN, Luminosity.BRIGHT, SaturationType.HIGH); + add(Biome.DEEP_OCEAN, 0x000030, (short) 24, Color.BLUE, Luminosity.DARK); + add(Biome.STONE_SHORE, 0xA2A284, (short) 25, Color.GREEN, Luminosity.DARK); + add(Biome.SNOWY_BEACH, 0xFAF0C0, (short) 26, Color.YELLOW, Luminosity.LIGHT); + add(Biome.BIRCH_FOREST, 0x307444, (short) 27, Color.GREEN, Luminosity.LIGHT); + add(Biome.BIRCH_FOREST_HILLS, 0x1F5F32, (short) 28, Color.GREEN, Luminosity.LIGHT); + add(Biome.DARK_FOREST, 0x40511A, (short) 29, Color.GREEN, Luminosity.DARK); + add(Biome.SNOWY_TAIGA, 0x31554A, (short) 30, Color.BLUE, Luminosity.LIGHT); + add(Biome.SNOWY_TAIGA_HILLS, 0x243F36, (short) 31, Color.BLUE, Luminosity.LIGHT); + add(Biome.GIANT_TREE_TAIGA, 0x596651, (short) 32, Color.ORANGE, Luminosity.LIGHT); + add(Biome.GIANT_TREE_TAIGA_HILLS, 0x454F3E, (short) 33, Color.ORANGE, Luminosity.LIGHT); + add(Biome.WOODED_MOUNTAINS, 0x507050, (short) 34, Color.MONOCHROME, Luminosity.BRIGHT); + add(Biome.SAVANNA, 0xBDB25F, (short) 35, Color.GREEN, Luminosity.LIGHT); + add(Biome.SAVANNA_PLATEAU, 0xA79D64, (short) 36, Color.GREEN, Luminosity.LIGHT); + add(Biome.BADLANDS, 0xD94515, (short) 37, Color.ORANGE, Luminosity.BRIGHT, SaturationType.MEDIUM); + add(Biome.WOODED_BADLANDS_PLATEAU, 0xB09765, (short) 38, Color.ORANGE, Luminosity.BRIGHT, SaturationType.HIGH); + add(Biome.BADLANDS_PLATEAU, 0xCA8C65, (short) 39, Color.ORANGE, Luminosity.BRIGHT, SaturationType.HIGH); + add(Biome.END_MIDLANDS, 0x8080FF, (short) 41, Color.YELLOW, Luminosity.LIGHT, SaturationType.LOW); + add(Biome.END_HIGHLANDS, 0x8080FF, (short) 42, Color.PURPLE, Luminosity.LIGHT, SaturationType.LOW); + add(Biome.END_BARRENS, 0x8080FF, (short) 43, Color.PURPLE, Luminosity.LIGHT, SaturationType.MEDIUM); + add(Biome.WARM_OCEAN, 0x0000AC, (short) 44, Color.BLUE, Luminosity.BRIGHT, SaturationType.LOW); + add(Biome.LUKEWARM_OCEAN, 0x000090, (short) 45, Color.BLUE, Luminosity.BRIGHT, SaturationType.MEDIUM); + add(Biome.COLD_OCEAN, 0x202070, (short) 46, Color.BLUE, Luminosity.BRIGHT, SaturationType.HIGH); + add(Biome.DEEP_WARM_OCEAN, 0x000050, (short) 47, Color.BLUE, Luminosity.DARK, SaturationType.LOW); + add(Biome.DEEP_LUKEWARM_OCEAN, 0x000040, (short) 48, Color.BLUE, Luminosity.DARK, SaturationType.MEDIUM); + add(Biome.DEEP_COLD_OCEAN, 0x202038, (short) 49, Color.BLUE, Luminosity.DARK, SaturationType.HIGH); + add(Biome.DEEP_FROZEN_OCEAN, 0x404090, (short) 50, Color.BLUE, Luminosity.LIGHT, SaturationType.LOW); + add(Biome.THE_VOID, 0x000000, (short) 127, Color.MONOCHROME, Luminosity.DARK); + add(Biome.SUNFLOWER_PLAINS, 0xB5DB88, (short) 129, Color.GREEN, Luminosity.LIGHT, SaturationType.LOW); + add(Biome.DESERT_LAKES, 0xFFBC40, (short) 130, Color.BLUE, Luminosity.LIGHT, SaturationType.LOW); + add(Biome.GRAVELLY_MOUNTAINS, 0x888888, (short) 131, Color.MONOCHROME, Luminosity.LIGHT); + add(Biome.FLOWER_FOREST, 0x2D8E49, (short) 132, Color.RED, Luminosity.LIGHT, SaturationType.LOW); + add(Biome.TAIGA_MOUNTAINS, 0x338E81, (short) 133, Color.GREEN, Luminosity.DARK, SaturationType.MEDIUM); + add(Biome.SWAMP_HILLS, 0x2FFFDA, (short) 134, Color.ORANGE, Luminosity.DARK, SaturationType.MEDIUM); + add(Biome.ICE_SPIKES, 0xB4DCDC, (short) 140, Color.BLUE, Luminosity.LIGHT, SaturationType.LOW); + add(Biome.MODIFIED_JUNGLE, 0x7BA331, (short) 149, Color.GREEN, Luminosity.BRIGHT, SaturationType.HIGH); + add(Biome.MODIFIED_JUNGLE_EDGE, 0x8AB33F, (short) 151, Color.GREEN, Luminosity.BRIGHT, SaturationType.HIGH); + add(Biome.TALL_BIRCH_FOREST, 0x589C6C, (short) 155, Color.GREEN, Luminosity.LIGHT); + add(Biome.TALL_BIRCH_HILLS, 0x47875A, (short) 156, Color.GREEN, Luminosity.LIGHT); + add(Biome.DARK_FOREST_HILLS, 0x687942, (short) 157, Color.GREEN, Luminosity.DARK); + add(Biome.SNOWY_TAIGA_MOUNTAINS, 0x597D72, (short) 158, Color.BLUE, Luminosity.LIGHT); + add(Biome.GIANT_SPRUCE_TAIGA, 0x818E79, (short) 160, Color.ORANGE, Luminosity.DARK, SaturationType.HIGH); + add(Biome.GIANT_SPRUCE_TAIGA_HILLS, 0x6D7766, (short) 161, Color.ORANGE, Luminosity.DARK, SaturationType.HIGH); + add(Biome.GRAVELLY_MOUNTAINS, 0x789878, (short) 162, Color.MONOCHROME, Luminosity.LIGHT); + add(Biome.SHATTERED_SAVANNA, 0xE5DA87, (short) 163, Color.ORANGE, Luminosity.LIGHT, SaturationType.HIGH); + add(Biome.SHATTERED_SAVANNA_PLATEAU, 0xCFC58C, (short) 164, Color.ORANGE, Luminosity.LIGHT, SaturationType.HIGH); + add(Biome.ERODED_BADLANDS, 0xFF6D3D, (short) 165, Color.ORANGE, Luminosity.LIGHT, SaturationType.HIGH); + add(Biome.MODIFIED_WOODED_BADLANDS_PLATEAU, 0xD8BF8D, (short) 166, Color.ORANGE, Luminosity.BRIGHT); + add(Biome.MODIFIED_BADLANDS_PLATEAU, 0xF2B48D, (short) 167, Color.ORANGE, Luminosity.BRIGHT); + add(Biome.BAMBOO_JUNGLE, 0x768E14, (short) 168, Color.GREEN, Luminosity.BRIGHT, SaturationType.HIGH); + add(Biome.BAMBOO_JUNGLE_HILLS, 0x3B470A, (short) 169, Color.GREEN, Luminosity.BRIGHT, SaturationType.HIGH); + add(Biome.SOUL_SAND_VALLEY, 0x5E3830, (short) 170, Color.BLUE, Luminosity.BRIGHT, SaturationType.MEDIUM); + add(Biome.CRIMSON_FOREST, 0xDD0808, (short) 171, Color.RED, Luminosity.DARK, SaturationType.HIGH); + add(Biome.WARPED_FOREST, 0x49907B, (short) 172, Color.BLUE, Luminosity.BRIGHT); + add(Biome.BASALT_DELTAS, 0x403636, (short) 173, Color.MONOCHROME, Luminosity.DARK); + } +}