New Studio Map Command

- Allows you to see a map of a world/pack (without being in the world)
- Chooses a random color for each biome if no color is chosen, based on vanilla derivative 

Dev note: 
- The biome at your cursor is not accurate when zoomed out
This commit is contained in:
StrangeOne101 2021-07-15 17:52:20 +12:00
parent c54a849243
commit ad6b746124
8 changed files with 138 additions and 83 deletions

View File

@ -4,42 +4,36 @@ 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.KSet;
import com.volmit.iris.util.PrecisionStopwatch;
import com.volmit.iris.util.RandomColor;
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.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.FlowLayout;
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.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;
public class MapVision extends JPanel {
@ -101,15 +95,15 @@ public class MapVision extends JPanel {
addMouseWheelListener((mouseWheelEvent) -> {
double oldScale = this.scale;
this.scale = Math.min(4, Math.max(scale + scale * mouseWheelEvent.getWheelRotation() * 0.2, 1));
double wx = getWidth();
double hy = getHeight();
double xScale = (mouseX - wx) / wx * 0.5;
double yScale = (mouseY - hy) / hy * 0.5;
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 / 2) * (oldScale - scale);
this.draggedOffsetY += yScale * (hy / 2) * (oldScale - scale);
}
/*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();
@ -165,7 +159,7 @@ public class MapVision extends JPanel {
}
@Override
public void componentShown(ComponentEvent e) { }
public void componentShown(ComponentEvent e) { }
@Override
public void componentHidden(ComponentEvent e) { }
@ -202,6 +196,18 @@ public class MapVision extends JPanel {
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
@ -238,8 +244,8 @@ public class MapVision extends JPanel {
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 * scale)) << 2;
int y = (int) (((int) ((mouseY - windowOffsetY))) + (-draggedOffsetY * scale)) << 2;
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;
@ -294,14 +300,12 @@ public class MapVision extends JPanel {
public void drawTile(Graphics gx, Tile tile) {
if (gx == null) return;
int x = (int) Math.round((tile.getX() << TILE_SIZE_R) / scale + offsetX);
int y = (int) Math.round((tile.getY() << TILE_SIZE_R) / scale + offsetY);
//int x = (int) ((tile.getX() * TILE_SIZE) / scale + offsetX);
//int y = (int) ((tile.getY() * TILE_SIZE) / scale + offsetY);
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) (TILE_SIZE % scale);
gx.drawImage(tile.getImage(), x, y, size, size, null);
int off = (int) Math.round((TILE_SIZE % scale));
gx.drawImage(tile.getImage(), x, y, size + off, size + off,null);
}
private Runnable sleepTask = new Runnable() {
@ -362,16 +366,13 @@ public class MapVision extends JPanel {
lastTileWidth = checkSizeX;
generateSpiral(newSize);
Set<Integer> checked = new HashSet<>();
Set<Integer> clone = new HashSet(visibleTiles.stream().map((t) ->
getTileId(t.getX(), t.getY()))
.collect(Collectors.toSet())); //Clone the visible tiles
Set<Tile> 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;
debugBorder[1] = -checkSizeY + centerTileY;
debugBorder[2] = checkSizeX + 1 + centerTileX;
debugBorder[3] = checkSizeY + 1 + centerTileY;
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
@ -379,10 +380,10 @@ public class MapVision extends JPanel {
short y = (short)(coords[1] + centerTileY);
//When it goes offscreen, don't queue the tile by continuing
if (Math.abs(coords[0]) > checkSizeX + 1) {
if (x > checkSizeX + centerTileX || x < -checkSizeX + centerTileX - 1) {
continue;
}
if (Math.abs(coords[1]) > checkSizeY + 1) {
if (y > checkSizeY + centerTileY || y < -checkSizeY + centerTileY - 1) {
continue;
}
@ -393,13 +394,16 @@ public class MapVision extends JPanel {
short[] c = getTileCoords(id);
queue(c[0], c[1]); //Queue for creation
} else {
checked.add(id);
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
}
}
}
clone.removeAll(checked); //Remove the tiles that we know are onscreen
queueForRemoval(clone);
queueForRemoval(toRemove); //Queue all tiles not on screen for removal
stopwatch.end();
roll.put(stopwatch.getMillis());
@ -457,11 +461,9 @@ public class MapVision extends JPanel {
/**
* Pend tiles for removal from the screen
*/
public void queueForRemoval(Collection<Integer> ids) {
public void queueForRemoval(Collection<Tile> tile) {
J.a(() -> {
for (int id : ids) {
Tile t = tiles.get(id);
for (Tile t : tile) {
if (t != null) {
visibleTiles.remove(t);
}
@ -471,10 +473,9 @@ public class MapVision extends JPanel {
//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 (int id : ids) {
Tile t = tiles.get(id);
for (Tile t : tile) {
if (t != null && !visibleTiles.contains(t)) {
tiles.remove(id);
tiles.remove(t);
}
}
@ -499,7 +500,7 @@ public class MapVision extends JPanel {
* @return
*/
public int getTileId(short tileX, short tileY) {
return tileX + tileY << 16;
return (tileX << 16) | (tileY & 0xFFFF);
}
/**
@ -508,7 +509,7 @@ public class MapVision extends JPanel {
* @return
*/
public short[] getTileCoords(int id) {
return new short[] {(short) (id & 0x0000FFFF), (short) (id >> 16)};
return new short[] {(short)(id >> 16), (short) id};
}
/**
@ -578,7 +579,7 @@ public class MapVision extends JPanel {
threadId++;
Thread t = new Thread(r);
t.setName("Iris Map Renderer " + threadId);
t.setPriority(Thread.MIN_PRIORITY);
t.setPriority(4);
t.setDaemon(true);
t.setUncaughtExceptionHandler((et, e) ->
{
@ -591,7 +592,7 @@ public class MapVision extends JPanel {
};
//Our thread pool that draws the tiles for us
private final ThreadPoolExecutor executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(8, factory);
private final ThreadPoolExecutor executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(3, factory);
}

View File

@ -1,5 +1,5 @@
package com.volmit.iris.map;
public enum RenderType {
BIOME_LAND, REGION, CAVE_LAND
BIOME_LAND, REGION, CAVE_LAND, HEIGHT
}

View File

@ -7,6 +7,7 @@ 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;
@ -55,17 +56,15 @@ public class Tile {
public boolean render(IrisComplex complex, RenderType type) {
BufferedImage newImage = new BufferedImage(128, 128, BufferedImage.TYPE_INT_RGB);
ProceduralStream stream;
BiFunction<Integer, Integer, Integer> getColor;
if (type == RenderType.BIOME_LAND) {
stream = complex.getLandBiomeStream();
getColor = (x, z) -> ((ProceduralStream<IrisBiome>)stream).get(x, z).getColor().getAsRGB();
getColor = (x, z) -> complex.getLandBiomeStream().get(x, z).getColor().getRGB();
} else if (type == RenderType.REGION) {
stream = complex.getRegionStream();
getColor = (x, z) -> ((ProceduralStream<IrisRegion>)stream).get(x, z).getColor().getColor().getRGB();
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 {
stream = complex.getCaveBiomeStream();
getColor = (x, z) -> ((ProceduralStream<IrisBiome>)stream).get(x, z).getColor().getAsRGB();
getColor = (x, z) -> complex.getCaveBiomeStream().get(x, z).getColor().getRGB();
}
for (int i = 0; i < 128; i++) {

View File

@ -92,7 +92,7 @@ public class IrisBiome extends IrisRegistrant implements IRare {
@DontObfuscate
@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
@DontObfuscate
@ -187,7 +187,7 @@ public class IrisBiome extends IrisRegistrant implements IRare {
private final transient AtomicCache<KMap<String, Integer>> genCacheMin = new AtomicCache<>();
private final transient AtomicCache<KList<IrisObjectPlacement>> surfaceObjectsCache = new AtomicCache<>(false);
private final transient AtomicCache<KList<IrisObjectPlacement>> carveObjectsCache = new AtomicCache<>(false);
private final transient AtomicCache<Color> cacheColor = new AtomicCache<>(true);
private final transient AtomicCache<Color> cacheColor = new AtomicCache<>();
private final transient AtomicCache<CNG> childrenCell = new AtomicCache<>();
private final transient AtomicCache<CNG> biomeGenerator = new AtomicCache<>();
private final transient AtomicCache<Integer> maxHeight = new AtomicCache<>();
@ -585,25 +585,28 @@ public class IrisBiome extends IrisRegistrant implements IRare {
return getLayers().get(0).get(rng, x, 0, z, idm);
}
public IrisColor getColor() {
if (this.color == null) {
RandomColor randomColor = new RandomColor(getName().hashCode());
if (this.getVanillaDerivative() == null) {
this.color = new IrisColor();
this.color.setRed(255).setGreen(255).setBlue(255);
Iris.warn("No vanilla biome found for " + getName());
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);
}
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);
Color newColor = new Color(newColorI);
this.color = new IrisColor();
this.color.setRed(newColor.getRed()).setBlue(newColor.getBlue()).setGreen(newColor.getGreen());
}
return this.color;
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());
}
});
}
}

View File

@ -11,6 +11,7 @@ import lombok.experimental.Accessors;
import java.awt.*;
@Deprecated(forRemoval = true)
@Accessors(chain = true)
@NoArgsConstructor
@Desc("Represents a color")

View File

@ -1,5 +1,6 @@
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;
@ -11,6 +12,9 @@ import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.awt.Color;
import java.util.Random;
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
@ -217,7 +221,7 @@ public class IrisRegion extends IrisRegistrant implements IRare {
@DontObfuscate
@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<KList<IrisObjectPlacement>> surfaceObjectsCache = new AtomicCache<>();
private final transient AtomicCache<KList<IrisObjectPlacement>> carveObjectsCache = new AtomicCache<>();
@ -233,6 +237,7 @@ public class IrisRegion extends IrisRegistrant implements IRare {
private final transient AtomicCache<CNG> lakeGen = new AtomicCache<>();
private final transient AtomicCache<CNG> riverGen = new AtomicCache<>();
private final transient AtomicCache<CNG> riverChanceGen = new AtomicCache<>();
private final transient AtomicCache<Color> cacheColor = new AtomicCache<>();
public String getName() {
return name;
@ -518,4 +523,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<IrisBiome> 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) {
}
}

View File

@ -91,10 +91,10 @@ 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 f;

View File

@ -130,6 +130,11 @@ public class RandomColor {
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();
}