diff --git a/pom.xml b/pom.xml index 166199b1f..c0310d534 100644 --- a/pom.xml +++ b/pom.xml @@ -91,7 +91,7 @@ org.polydev gaea - 1.9.4 + 1.9.6 javax.vecmath diff --git a/src/main/java/com/dfsek/terra/TerraChunkGenerator.java b/src/main/java/com/dfsek/terra/TerraChunkGenerator.java index 50639abf4..9464d0d3a 100644 --- a/src/main/java/com/dfsek/terra/TerraChunkGenerator.java +++ b/src/main/java/com/dfsek/terra/TerraChunkGenerator.java @@ -59,6 +59,7 @@ public class TerraChunkGenerator extends GaeaChunkGenerator { return Collections.emptyList(); } + @Override public org.polydev.gaea.biome.BiomeGrid getBiomeGrid(World world) { return TerraBiomeGrid.fromWorld(world); diff --git a/src/main/java/com/dfsek/terra/TerraCommand.java b/src/main/java/com/dfsek/terra/TerraCommand.java index 364c0fd2e..c78e4fda0 100644 --- a/src/main/java/com/dfsek/terra/TerraCommand.java +++ b/src/main/java/com/dfsek/terra/TerraCommand.java @@ -2,9 +2,11 @@ package com.dfsek.terra; import com.dfsek.terra.biome.TerraBiomeGrid; import com.dfsek.terra.biome.UserDefinedBiome; +import com.dfsek.terra.config.WorldConfig; import com.dfsek.terra.config.genconfig.BiomeConfig; import com.dfsek.terra.config.ConfigUtil; import com.dfsek.terra.config.genconfig.OreConfig; +import com.dfsek.terra.image.WorldImageGenerator; import org.bukkit.block.Block; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; @@ -15,6 +17,7 @@ import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.polydev.gaea.profiler.WorldProfiler; +import java.io.File; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -71,7 +74,42 @@ public class TerraCommand implements CommandExecutor, TabExecutor { } ore.doVein(bl.getLocation(), new Random()); return true; - + case "image": + if("render".equals(args[1])) { + if(! (sender instanceof Player)) { + sender.sendMessage("Command is for players only."); + return true; + } + Player pl = (Player) sender; + if(args.length != 4) return false; + try { + WorldImageGenerator g = new WorldImageGenerator(pl.getWorld(), Integer.parseInt(args[2]), Integer.parseInt(args[3])); + g.drawWorld(pl.getLocation().getBlockX(), pl.getLocation().getBlockZ()); + File file = new File(Terra.getInstance().getDataFolder() + File.separator + "map_export" + File.separator + "map_" + System.currentTimeMillis() + ".png"); + file.mkdirs(); + file.createNewFile(); + g.save(file); + sender.sendMessage("Saved image to " + file.getPath()); + return true; + } catch(Exception e) { + e.printStackTrace(); + return false; + } + } else if("gui".equals(args[1])) { + if(! (sender instanceof Player)) { + sender.sendMessage("Command is for players only."); + return true; + } + Player pl = (Player) sender; + try { + if("raw".equals(args[2])) WorldConfig.fromWorld(pl.getWorld()).imageLoader.debug(false, pl.getWorld()); + else if("step".equals(args[2])) WorldConfig.fromWorld(pl.getWorld()).imageLoader.debug(true, pl.getWorld()); + else return false; + return true; + } catch(NullPointerException e) { + return false; + } + } } return false; } diff --git a/src/main/java/com/dfsek/terra/biome/BiomeZone.java b/src/main/java/com/dfsek/terra/biome/BiomeZone.java index cac6d923e..1dd8ef3ae 100644 --- a/src/main/java/com/dfsek/terra/biome/BiomeZone.java +++ b/src/main/java/com/dfsek/terra/biome/BiomeZone.java @@ -22,7 +22,7 @@ public class BiomeZone { private final ImageLoader imageLoader; private final boolean useImage; private final ImageLoader.Channel channel; - private BiomeZone(World w, float freq) { + private BiomeZone(World w) { this.w = w; this.noise = new FastNoise((int) w.getSeed()+2); this.noise.setNoiseType(FastNoise.NoiseType.SimplexFractal); @@ -45,8 +45,16 @@ public class BiomeZone { return grids[NormalizationUtil.normalize(useImage ? Objects.requireNonNull(imageLoader).getNoiseVal(x, z, channel) : noise.getNoise(x, z), 32)]; } - protected static BiomeZone fromWorld(World w) { + public int getNoise(int x, int z) { + return NormalizationUtil.normalize(useImage ? Objects.requireNonNull(imageLoader).getNoiseVal(x, z, channel) : noise.getNoise(x, z), 32); + } + + public double getRawNoise(int x, int z) { + return useImage ? Objects.requireNonNull(imageLoader).getNoiseVal(x, z, channel) : noise.getNoise(x, z); + } + + public static BiomeZone fromWorld(World w) { if(zones.containsKey(w)) return zones.get(w); - else return new BiomeZone(w, WorldConfig.fromWorld(w).zoneFreq); + else return new BiomeZone(w); } } diff --git a/src/main/java/com/dfsek/terra/biome/TerraBiomeGrid.java b/src/main/java/com/dfsek/terra/biome/TerraBiomeGrid.java index 4f3a9bac4..8beb7e936 100644 --- a/src/main/java/com/dfsek/terra/biome/TerraBiomeGrid.java +++ b/src/main/java/com/dfsek/terra/biome/TerraBiomeGrid.java @@ -36,4 +36,8 @@ public class TerraBiomeGrid extends BiomeGrid { public Biome getBiome(Location l) { return getBiome(l.getBlockX(), l.getBlockZ()); } + + public UserDefinedGrid getGrid(int x, int z) { + return (UserDefinedGrid) BiomeZone.fromWorld(w).getGrid(x, z); + } } diff --git a/src/main/java/com/dfsek/terra/biome/UserDefinedGrid.java b/src/main/java/com/dfsek/terra/biome/UserDefinedGrid.java index 157b4d850..4bd4f5ed0 100644 --- a/src/main/java/com/dfsek/terra/biome/UserDefinedGrid.java +++ b/src/main/java/com/dfsek/terra/biome/UserDefinedGrid.java @@ -7,6 +7,7 @@ import org.bukkit.Location; import org.bukkit.World; import org.polydev.gaea.biome.Biome; import org.polydev.gaea.biome.BiomeGrid; +import org.polydev.gaea.biome.NormalizationUtil; public class UserDefinedGrid extends BiomeGrid { private final ImageLoader imageLoader; @@ -37,9 +38,9 @@ public class UserDefinedGrid extends BiomeGrid { @Override public Biome getBiome(int x, int z) { if(fromImage) { - int xi = imageLoader.getChannel(x, z, channelX); - int zi = imageLoader.getChannel(x, z, channelZ); - return super.getGrid()[getSizeX() * (xi/256)][getSizeZ() * (zi/256)]; + double xi = imageLoader.getNoiseVal(x, z, channelX); + double zi = imageLoader.getNoiseVal(x, z, channelZ); + return super.getGrid()[NormalizationUtil.normalize(xi, getSizeX())][NormalizationUtil.normalize(zi, getSizeZ())]; } return super.getBiome(x, z); } diff --git a/src/main/java/com/dfsek/terra/config/WorldConfig.java b/src/main/java/com/dfsek/terra/config/WorldConfig.java index e17e0c858..c1e8b19c4 100644 --- a/src/main/java/com/dfsek/terra/config/WorldConfig.java +++ b/src/main/java/com/dfsek/terra/config/WorldConfig.java @@ -6,6 +6,7 @@ import com.dfsek.terra.biome.UserDefinedGrid; import com.dfsek.terra.config.genconfig.BiomeConfig; import com.dfsek.terra.config.genconfig.BiomeGridConfig; import com.dfsek.terra.image.ImageLoader; +import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.FileConfiguration; @@ -64,45 +65,54 @@ public class WorldConfig { FileUtils.copyInputStreamToFile(Objects.requireNonNull(main.getResource("world.yml")), configFile); } config.load(configFile); + + + // Get values from config. + seaLevel = config.getInt("sea-level", 63); + zoneFreq = 1f/config.getInt("frequencies.zone", 1536); + freq1 = 1f/config.getInt("frequencies.grid-1", 256); + freq2 = 1f/config.getInt("frequencies.grid-2", 512); + fromImage = config.getBoolean("image.use-image", false); + biomeXChannel = ImageLoader.Channel.valueOf(Objects.requireNonNull(config.getString("image.channels.biome-x", "red")).toUpperCase()); + biomeZChannel = ImageLoader.Channel.valueOf(Objects.requireNonNull(config.getString("image.channels.biome-z", "green")).toUpperCase()); + if(biomeZChannel.equals(biomeXChannel)) throw new InvalidConfigurationException("2 objects share the same image channels: biome-x and biome-z"); + zoneChannel = ImageLoader.Channel.valueOf(Objects.requireNonNull(config.getString("image.channels.zone", "blue")).toUpperCase()); + if(zoneChannel.equals(biomeXChannel) || zoneChannel.equals(biomeZChannel)) throw new InvalidConfigurationException("2 objects share the same image channels: zone and biome-x/z"); + if(fromImage) { + try { + imageLoader = new ImageLoader(new File(Objects.requireNonNull(config.getString("image.image-location")))); + Bukkit.getLogger().info("[Terra] Loading world from image."); + } catch(IOException | NullPointerException e) { + e.printStackTrace(); + fromImage = false; + } + } + + + configs.put(w, this); // WorldConfig must be included in map before Grids are loaded. + + for(int i = 0; i < 32; i++) { + String partName = config.getStringList("grids").get(i); + if(partName.startsWith("BIOME:")) { + UserDefinedBiome[][] temp = new UserDefinedBiome[16][16]; + UserDefinedBiome b = BiomeConfig.fromID(partName.substring(6)).getBiome(); + for(int x = 0; x < 16; x++) { + for(int z = 0; z < 16; z++) { + temp[x][z] = b; + } + } + definedGrids[i] = new UserDefinedGrid(w, freq1, freq2, temp); + main.getLogger().info("Loaded single-biome grid " + partName); + } else definedGrids[i] = BiomeGridConfig.getBiomeGrids().get(partName).getGrid(w); + } + } catch(IOException | InvalidConfigurationException e) { e.printStackTrace(); main.getLogger().severe("Unable to load configuration for world " + w + "."); } - // Get values from config. - seaLevel = config.getInt("sea-level", 63); - zoneFreq = 1f/config.getInt("frequencies.zone", 1536); - freq1 = 1f/config.getInt("frequencies.grid-1", 256); - freq2 = 1f/config.getInt("frequencies.grid-2", 512); - fromImage = config.getBoolean("image.use-image", false); - biomeXChannel = ImageLoader.Channel.valueOf(Objects.requireNonNull(config.getString("image.channels.biome-x", "red")).toUpperCase()); - biomeZChannel = ImageLoader.Channel.valueOf(Objects.requireNonNull(config.getString("image.channels.biome-z", "green")).toUpperCase()); - zoneChannel = ImageLoader.Channel.valueOf(Objects.requireNonNull(config.getString("image.channels.zone", "blue")).toUpperCase()); - try { - imageLoader = new ImageLoader(new File(Objects.requireNonNull(config.getString("image.image-location")))); - } catch(IOException | NullPointerException e) { - e.printStackTrace(); - fromImage = false; - } - configs.put(w, this); // WorldConfig must be included in map before Grids are loaded. - - for(int i = 0; i < 32; i++) { - String partName = config.getStringList("grids").get(i); - if(partName.startsWith("BIOME:")) { - UserDefinedBiome[][] temp = new UserDefinedBiome[16][16]; - UserDefinedBiome b = BiomeConfig.fromID(partName.substring(6)).getBiome(); - for(int x = 0; x < 16; x++) { - for(int z = 0; z < 16; z++) { - temp[x][z] = b; - } - } - definedGrids[i] = new UserDefinedGrid(w, freq1, freq2, temp); - main.getLogger().info("Loaded single-biome grid " + partName); - } else definedGrids[i] = BiomeGridConfig.getBiomeGrids().get(partName).getGrid(w); - } - main.getLogger().info("World load complete. Time elapsed: " + ((double) (System.nanoTime() - start)) / 1000000 + "ms"); diff --git a/src/main/java/com/dfsek/terra/image/DebugFrame.java b/src/main/java/com/dfsek/terra/image/DebugFrame.java new file mode 100644 index 000000000..32f96062d --- /dev/null +++ b/src/main/java/com/dfsek/terra/image/DebugFrame.java @@ -0,0 +1,49 @@ +package com.dfsek.terra.image; + +import com.dfsek.terra.Terra; +import com.dfsek.terra.biome.TerraBiomeGrid; +import com.dfsek.terra.biome.UserDefinedBiome; +import com.dfsek.terra.config.genconfig.BiomeConfig; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.image.BufferedImage; + +public class DebugFrame extends JFrame implements ActionListener { + private final int x; + private final int z; + private final BufferedImage img; + public DebugFrame(BufferedImage image, String s) { + super(s); + this.x = image.getWidth(); + this.z = image.getHeight(); + this.img = image; + new Timer(500, this).start(); + } + + @Override + public void paint(Graphics g) { + super.paintComponents(g); + for(Player p : Bukkit.getOnlinePlayers()) { + int xp = (int) (((double) Math.floorMod(p.getLocation().getBlockX(), x)/x)*getWidth()); + int zp = (int) (((double) Math.floorMod(p.getLocation().getBlockZ(), z)/z)*getHeight()); + g.setColor(new Color(255, 255, 255, 64)); + g.drawRect(xp+10, zp-5, 20, 20); + g.setColor(Color.BLACK); + g.drawString(p.getName(), xp+15, zp); + g.drawString(BiomeConfig.fromBiome((UserDefinedBiome) TerraBiomeGrid.fromWorld(p.getWorld()).getBiome(p.getLocation())).getID(), xp+15, zp+15); + g.fillOval(xp, zp, 10, 10); + g.setColor(Color.RED); + g.fillOval(xp+3, zp+3, 5, 5); + } + } + + @Override + public void actionPerformed(ActionEvent e) { + this.repaint(); + } +} diff --git a/src/main/java/com/dfsek/terra/image/DebugGUI.java b/src/main/java/com/dfsek/terra/image/DebugGUI.java new file mode 100644 index 000000000..b438fce6b --- /dev/null +++ b/src/main/java/com/dfsek/terra/image/DebugGUI.java @@ -0,0 +1,28 @@ +package com.dfsek.terra.image; + +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; + +public class DebugGUI extends Thread { + + private final BufferedImage img; + public DebugGUI(BufferedImage img) { + this.img = img; + } + @Override + public void run() { + DebugFrame frame = new DebugFrame(img, "Image2Map Debug GUI"); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + frame.setSize(1000, 1000); + frame.setResizable(false); + ImageIcon imageIcon = new ImageIcon(img.getScaledInstance(1000, 1000, Image.SCALE_SMOOTH)); + JLabel jLabel = new JLabel(); + jLabel.setIcon(imageIcon); + frame.getContentPane().add(jLabel, BorderLayout.CENTER); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + +} diff --git a/src/main/java/com/dfsek/terra/image/ImageLoader.java b/src/main/java/com/dfsek/terra/image/ImageLoader.java index 84defcd18..f8d4e5bc9 100644 --- a/src/main/java/com/dfsek/terra/image/ImageLoader.java +++ b/src/main/java/com/dfsek/terra/image/ImageLoader.java @@ -1,5 +1,11 @@ package com.dfsek.terra.image; +import com.dfsek.terra.biome.BiomeZone; +import com.dfsek.terra.biome.TerraBiomeGrid; +import com.dfsek.terra.config.genconfig.BiomeConfig; +import org.bukkit.World; +import org.polydev.gaea.biome.NormalizationUtil; + import java.awt.*; import java.awt.image.BufferedImage; import java.io.File; @@ -35,9 +41,35 @@ public class ImageLoader { } } + public void debug(boolean genStep, World w) { + BufferedImage newImg = copyImage(image); + TerraBiomeGrid tb = TerraBiomeGrid.fromWorld(w); + BiomeZone z = BiomeZone.fromWorld(w); + if(genStep) { + for(int x = 0; x < newImg.getWidth(); x++) { + for(int y = 0; y < newImg.getHeight(); y++) { + float[] noise = tb.getGrid(x, y).getRawNoise(x, y); + newImg.setRGB(x, y, new Color((int) (NormalizationUtil.normalize(noise[0], tb.getGrid(x, y).getSizeX()) * ((double) 255/tb.getGrid(x, y).getSizeX())), + (int) (NormalizationUtil.normalize(noise[1], tb.getGrid(x, y).getSizeZ()) * ((double) 255/tb.getGrid(x, y).getSizeZ())), + (int) (z.getNoise(x, y) * ((double) 255/32))) + .getRGB()); + } + } + } + DebugGUI debugGUI = new DebugGUI(newImg); + debugGUI.start(); + } + public double getNoiseVal(int x, int y, Channel channel) { return ((double) (getChannel(x, y, channel) - 128)/128)*inverseRoot2; } + private static BufferedImage copyImage(BufferedImage source){ + BufferedImage b = new BufferedImage(source.getWidth(), source.getHeight(), source.getType()); + Graphics g = b.getGraphics(); + g.drawImage(source, 0, 0, null); + g.dispose(); + return b; + } public enum Channel { RED, GREEN, BLUE, ALPHA diff --git a/src/main/java/com/dfsek/terra/image/WorldImageGenerator.java b/src/main/java/com/dfsek/terra/image/WorldImageGenerator.java new file mode 100644 index 000000000..c86bf2451 --- /dev/null +++ b/src/main/java/com/dfsek/terra/image/WorldImageGenerator.java @@ -0,0 +1,44 @@ +package com.dfsek.terra.image; + +import com.dfsek.terra.biome.BiomeZone; +import com.dfsek.terra.biome.TerraBiomeGrid; +import com.dfsek.terra.config.WorldConfig; +import org.bukkit.World; +import org.polydev.gaea.biome.NormalizationUtil; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +public class WorldImageGenerator { + private final World w; + private final BufferedImage draw; + public WorldImageGenerator(World w, int width, int height) { + draw = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + this.w = w; + } + public void drawWorld(int centerX, int centerZ) { + TerraBiomeGrid tb = TerraBiomeGrid.fromWorld(w); + int imY = 0; + for(int y = centerZ - (draw.getHeight()/2); y < centerZ + (draw.getHeight()/2); y++) { + int imX = 0; + for(int x = centerX - (draw.getWidth()/2); x < centerX + (draw.getWidth()/2); x++) { + int zone = NormalizationUtil.normalize(BiomeZone.fromWorld(w).getRawNoise(x, y), 256); + float[] noise = tb.getGrid(x, y).getRawNoise(x, y); + Color c = new Color(NormalizationUtil.normalize(noise[0], 256), NormalizationUtil.normalize(noise[1], 256), zone); + draw.setRGB(imX, imY, c.getRGB()); + imX++; + } + imY++; + } + } + public void save(File file) { + try { + ImageIO.write(draw, "png", file); + } catch(IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/resources/terra.commodore b/src/main/resources/terra.commodore index 749f705f3..57c59cafc 100644 --- a/src/main/resources/terra.commodore +++ b/src/main/resources/terra.commodore @@ -13,4 +13,15 @@ terra { ore { ore brigadier:string single_word; } + image { + render { + size_x brigadier:integer { + size_z brigadier:integer; + } + } + gui { + raw; + step; + } + } } \ No newline at end of file diff --git a/src/test/java/DistributionTest.java b/src/test/java/DistributionTest.java index b0d4c7bcd..8a31f24e4 100644 --- a/src/test/java/DistributionTest.java +++ b/src/test/java/DistributionTest.java @@ -19,7 +19,7 @@ public class DistributionTest { noise.setFrequency(0.02f); noise.setFractalOctaves(4); System.out.println(noise); - int attempts = 3; + int attempts = 8; int[] numbers = new int[attempts]; double min = Integer.MAX_VALUE; double max = Integer.MIN_VALUE; diff --git a/test.sh b/test.sh index 62eef0697..4312c9a86 100755 --- a/test.sh +++ b/test.sh @@ -26,7 +26,7 @@ cp $DIRECTORY/prod/$PROJECT.jar $DIRECTORY/server/plugins/$PROJECT.jar cd $DIRECTORY/server || exit if ! test -f "paperclip.jar"; then echo "Paper not found. Downloading now." - wget https://papermc.io/api/v1/paper/1.16.2/latest/download -O paperclip.jar + wget https://papermc.io/api/v1/paper/1.16.3/latest/download -O paperclip.jar fi if [ -z "$(grep true eula.txt 2>/dev/null)" ]; then echo