From 98a26a40b1fea0ea369a5d031e0735b1132aec5c Mon Sep 17 00:00:00 2001 From: Daniel Mills Date: Fri, 18 Oct 2019 13:56:01 -0400 Subject: [PATCH] f --- src/main/java/ninja/bytecode/iris/Iris.java | 106 +++++- .../ninja/bytecode/iris/IrisGenerator.java | 131 +++---- .../bytecode/iris/ParallelChunkGenerator.java | 7 +- .../ninja/bytecode/iris/PerformanceMode.java | 1 + .../java/ninja/bytecode/iris/Settings.java | 36 +- .../iris/atomics/AtomicChunkData.java | 50 +-- .../ninja/bytecode/iris/gen/GenLayer.java | 5 +- .../ninja/bytecode/iris/gen/GenLayerBase.java | 55 ++- .../bytecode/iris/gen/GenLayerBiome.java | 97 ------ .../bytecode/iris/gen/GenLayerDeepOcean.java | 5 +- .../bytecode/iris/gen/GenLayerFracture.java | 48 +++ .../bytecode/iris/gen/GenLayerMountains.java | 36 ++ .../iris/gen/GenLayerSuperSample.java | 81 +++++ .../ninja/bytecode/iris/util/Catalyst12.java | 54 +++ .../ninja/bytecode/iris/util/RealBiome.java | 323 ++---------------- voronoi.png | Bin 0 -> 24186 bytes 16 files changed, 479 insertions(+), 556 deletions(-) delete mode 100644 src/main/java/ninja/bytecode/iris/gen/GenLayerBiome.java create mode 100644 src/main/java/ninja/bytecode/iris/gen/GenLayerFracture.java create mode 100644 src/main/java/ninja/bytecode/iris/gen/GenLayerMountains.java create mode 100644 src/main/java/ninja/bytecode/iris/gen/GenLayerSuperSample.java create mode 100644 src/main/java/ninja/bytecode/iris/util/Catalyst12.java create mode 100644 voronoi.png diff --git a/src/main/java/ninja/bytecode/iris/Iris.java b/src/main/java/ninja/bytecode/iris/Iris.java index 4201e6161..cd054a5da 100644 --- a/src/main/java/ninja/bytecode/iris/Iris.java +++ b/src/main/java/ninja/bytecode/iris/Iris.java @@ -1,46 +1,63 @@ package ninja.bytecode.iris; import java.util.UUID; +import java.util.function.Function; import org.bukkit.Bukkit; +import org.bukkit.ChatColor; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.WorldCreator; +import org.bukkit.block.Biome; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.generator.ChunkGenerator; import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.util.Vector; +import ninja.bytecode.iris.util.RealBiome; +import ninja.bytecode.shuriken.bench.Profiler; +import ninja.bytecode.shuriken.collections.GMap; import ninja.bytecode.shuriken.collections.GSet; +import ninja.bytecode.shuriken.execution.J; import ninja.bytecode.shuriken.execution.TaskExecutor; +import ninja.bytecode.shuriken.format.F; +import ninja.bytecode.shuriken.math.M; public class Iris extends JavaPlugin implements Listener { - public static TaskExecutor noisePool; + public static Profiler profiler; + public static TaskExecutor genPool; public static IrisGenerator gen; public static Settings settings; public static Iris instance; + public static GMap>> values; public void onEnable() { + profiler = new Profiler(512); + values = new GMap<>(); instance = this; settings = new Settings(); gen = new IrisGenerator(); - noisePool = new TaskExecutor(settings.performance.threadCount, settings.performance.threadPriority, "Iris Noise Generator"); + genPool = new TaskExecutor(getTC(), settings.performance.threadPriority, "Iris Generator"); getServer().getPluginManager().registerEvents((Listener) this, this); - + // Debug world regens GSet ws = new GSet<>(); + World w = createIrisWorld(); for(Player i : Bukkit.getOnlinePlayers()) { + Location m = i.getLocation(); ws.add(i.getWorld().getName()); - i.teleport(new Location(w, 0, 256, 0)); + i.teleport(new Location(w, m.getX(), m.getY(), m.getZ(), m.getYaw(), m.getPitch())); i.setFlying(true); - i.setGameMode(GameMode.CREATIVE); + i.setGameMode(GameMode.SPECTATOR); } for(String i : ws) @@ -49,9 +66,30 @@ public class Iris extends JavaPlugin implements Listener } } + private int getTC() + { + switch(settings.performance.performanceMode) + { + case HALF_CPU: + return Math.max(Runtime.getRuntime().availableProcessors() / 2, 1); + case MATCH_CPU: + return Runtime.getRuntime().availableProcessors(); + case SINGLE_THREADED: + return 1; + case UNLIMITED: + return -1; + case EXPLICIT: + return settings.performance.threadCount; + default: + break; + } + + return Math.max(Runtime.getRuntime().availableProcessors() / 2, 1); + } + public void onDisable() { - noisePool.close(); + genPool.close(); } @Override @@ -74,6 +112,52 @@ public class Iris extends JavaPlugin implements Listener wold.setAutoSave(false); Bukkit.unloadWorld(wold, false); } + + if(e.getMessage().toLowerCase().equals("/iris info")) + { + e.setCancelled(true); + sendInfo(e.getPlayer()); + + for(Biome i : Biome.values()) + { + J.attempt(() -> System.out.print(new RealBiome(i))); + } + } + } + + private void sendInfo(Player player) + { + for(int i = 0; i < 18; i++) + { + player.sendMessage(""); + } + + GMap> w = values.get(player.getWorld().getName()); + for(String i : w.k()) + { + double value = w.get(i).apply(player.getLocation().toVector()); + String p = i.substring(0, 2); + String v = value + ""; + + if(p.startsWith("%")) + { + v = F.pc(value, Integer.valueOf(p.substring(1))); + } + + if(p.startsWith("D")) + { + v = F.f(value, Integer.valueOf(p.substring(1))); + } + + else if(p.startsWith("^")) + { + double c = M.percentRange(value, -11, 37); + double f = 32 + (c * (1.8)); + v = F.f(c, Integer.valueOf(p.substring(1))) + " \u00B0C / " + F.f(f, Integer.valueOf(p.substring(1))) + " \u00B0F"; + } + + player.sendMessage(ChatColor.GREEN + i.substring(2) + ": " + ChatColor.RESET + ChatColor.WHITE + ChatColor.BOLD + v); + } } private World createIrisWorld() @@ -85,4 +169,14 @@ public class Iris extends JavaPlugin implements Listener ww.setSpawnLocation(0, 256, 0); return ww; } + + public static void v(String w, String t, Function d) + { + if(!values.containsKey(w)) + { + values.put(w, new GMap<>()); + } + + values.get(w).put(t, d); + } } diff --git a/src/main/java/ninja/bytecode/iris/IrisGenerator.java b/src/main/java/ninja/bytecode/iris/IrisGenerator.java index e3ba7be12..119efd076 100644 --- a/src/main/java/ninja/bytecode/iris/IrisGenerator.java +++ b/src/main/java/ninja/bytecode/iris/IrisGenerator.java @@ -1,19 +1,27 @@ package ninja.bytecode.iris; +import java.awt.Polygon; +import java.awt.geom.Point2D; +import java.awt.geom.Point2D.Double; +import java.util.List; import java.util.Random; -import org.bukkit.Chunk; +import org.apache.logging.log4j.core.layout.GelfLayout; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Biome; +import org.bukkit.generator.BlockPopulator; import org.bukkit.util.Vector; +import org.bukkit.util.noise.PerlinNoiseGenerator; +import net.minecraft.server.v1_12_R1.GenLayer; +import net.minecraft.server.v1_12_R1.WorldProviderNormal; import ninja.bytecode.iris.gen.GenLayerBase; -import ninja.bytecode.iris.gen.GenLayerBiome; -import ninja.bytecode.iris.gen.GenLayerDeepOcean; +import ninja.bytecode.iris.gen.GenLayerSuperSample; import ninja.bytecode.iris.gen.IGenLayer; -import ninja.bytecode.iris.util.RealBiome; import ninja.bytecode.shuriken.collections.GList; +import ninja.bytecode.shuriken.math.CNG; +import ninja.bytecode.shuriken.math.M; import ninja.bytecode.shuriken.math.RNG; public class IrisGenerator extends ParallelChunkGenerator @@ -23,119 +31,66 @@ public class IrisGenerator extends ParallelChunkGenerator private MB SAND = new MB(Material.SAND); private MB BEDROCK = new MB(Material.BEDROCK); private GList genLayers; - private GenLayerBiome glBiome; private GenLayerBase glBase; + private GenLayerSuperSample glSuperSample; private int waterLevel = 127; private GList updates = new GList<>(); - - public void doUpdates(Chunk c) - { - for(Vector i : updates) - { - c.getBlock(i.getBlockX(), i.getBlockY(), i.getBlockZ()).getState().update(true); - } - - updates.clear(); - } + private String wf; @Override public void onInit(World world, Random random) { + wf = world.getName(); updates = new GList<>(); genLayers = new GList<>(); RNG rng = new RNG(world.getSeed()); - genLayers.add(glBiome = new GenLayerBiome(world, random, rng.nextRNG())); - genLayers.add(glBase = new GenLayerBase(world, random, rng.nextRNG())); - genLayers.add(new GenLayerDeepOcean(world, random, rng.nextRNG())); + genLayers.add(glBase = new GenLayerBase(this, world, random, rng.nextRNG())); + genLayers.add(glSuperSample = new GenLayerSuperSample(this, world, random, rng.nextRNG())); } public int getHeight(double dx, double dz) { - double noise = 0.5; + double height = M.clip(glSuperSample.getSuperSampledHeight(dx, dz), 0D, 1D); + + return (int) (height * 253); + } + + public double getRawHeight(double dx, double dz) + { + double noise = 0 + Iris.settings.gen.baseHeight; for(IGenLayer i : genLayers) { noise = i.generateLayer(noise, dx, dz); } - double n = noise * 250; - n = n > 254 ? 254 : n; - n = n < 0 ? 0 : n; - - return (int) n; + return M.clip(noise, 0D, 1D); } @Override - public Biome genColumn(int wx, int wz, int x, int z) + public Biome genColumn(int wxx, int wzx, int x, int z) { + int wx = (int) Math.round((double) wxx * Iris.settings.gen.horizontalZoom); + int wz = (int) Math.round((double) wzx * Iris.settings.gen.horizontalZoom); int height = getHeight(wx, wz); - double temp = glBiome.getTemperature(wx, wz, height); - RealBiome b = glBiome.getBiome(wx, wz, temp, height); - boolean underwater = height < waterLevel; - - // Change biome to ocean / deep ocean if underwater height - if(underwater) + + for(int i = 0; i < height; i++) { - b = RealBiome.biomes[Biome.OCEAN.ordinal()]; - } - - if(height > 122 && height < 128 + (temp * 1.5) + (glBase.scatter(wx, wx * wz, wz) * 3.35)) - { - b = RealBiome.biomes[Biome.BEACHES.ordinal()]; - } - - for(int i = 0; i < Math.max(height, waterLevel); i++) - { - MB mb = AIR; - - // Bedrockify - if(i == 0 || (!Iris.settings.gen.flatBedrock && ((i == 1 && glBase.scatterChance(wx, i, wz, 0.45))))) - { - mb = BEDROCK; - } - - // Surface blocks - else if(i == height - 1) - { - mb = b.surface(wx, i, wz, glBase); - } - - // Dirt Blocks - else if(!underwater && i > height - glBase.scatterInt(wx, i, wz, 12)) - { - mb = b.dirt(wx, i, wz, glBase); - } - - // Create Water blocks - else if(i >= height && underwater) - { - mb = WATER; - } - - // Below Dirt - else - { - mb = b.rock(wx, i, wz, glBase); - } - - if(mb.equals(AIR)) - { - continue; - } + MB mb = new MB(Material.STONE); setBlock(x, i, z, mb.material, mb.data); } - MB v = b.getSurfaceDecoration(); - - if(v != null && underwater == b.isWater() && (underwater ? height < 125 : true)) - { - setBlock(x, height, z, v.material, v.data); - } - - return b.getBiome(); + return Biome.PLAINS; } + @Override + public List getDefaultPopulators(World world) + { + GList p = new GList(); + + return p; + } public int pick(int max, double noise) { @@ -146,4 +101,10 @@ public class IrisGenerator extends ParallelChunkGenerator { return array[pick(array.length, noise)]; } + + @Override + public void onInitChunk(World world, int x, int z, Random random) + { + + } } \ No newline at end of file diff --git a/src/main/java/ninja/bytecode/iris/ParallelChunkGenerator.java b/src/main/java/ninja/bytecode/iris/ParallelChunkGenerator.java index d201f339f..ce2bc7a6c 100644 --- a/src/main/java/ninja/bytecode/iris/ParallelChunkGenerator.java +++ b/src/main/java/ninja/bytecode/iris/ParallelChunkGenerator.java @@ -40,7 +40,7 @@ public abstract class ParallelChunkGenerator extends ChunkGenerator ready = true; } - tg = Iris.noisePool.startWork(); + tg = Iris.genPool.startWork(); for(i = 0; i < 16; i++) { @@ -57,13 +57,14 @@ public abstract class ParallelChunkGenerator extends ChunkGenerator } } + onInitChunk(world, x, z, random); TaskResult r = tg.execute(); rs.put(r.timeElapsed); Shuriken.profiler.stop("chunkgen-" + world.getName()); if(cl.flip()) { - System.out.print("Avg: " + F.duration(rs.getAverage(), 2) + " " + F.duration(rs.getMax(), 2) + " / " + F.duration(rs.getMedian(), 2) + " / " + F.duration(rs.getMin(), 2)); + System.out.println("Total MS: " + F.duration(rs.getAverage(), 2)); } } @@ -82,6 +83,8 @@ public abstract class ParallelChunkGenerator extends ChunkGenerator } public abstract void onInit(World world, Random random); + + public abstract void onInitChunk(World world, int x, int z, Random random); public abstract Biome genColumn(int wx, int wz, int x, int z); diff --git a/src/main/java/ninja/bytecode/iris/PerformanceMode.java b/src/main/java/ninja/bytecode/iris/PerformanceMode.java index 802cdf5e0..67ec8fdd9 100644 --- a/src/main/java/ninja/bytecode/iris/PerformanceMode.java +++ b/src/main/java/ninja/bytecode/iris/PerformanceMode.java @@ -6,4 +6,5 @@ public enum PerformanceMode UNLIMITED, MATCH_CPU, HALF_CPU, + EXPLICIT, } diff --git a/src/main/java/ninja/bytecode/iris/Settings.java b/src/main/java/ninja/bytecode/iris/Settings.java index 75890a14a..21698b13d 100644 --- a/src/main/java/ninja/bytecode/iris/Settings.java +++ b/src/main/java/ninja/bytecode/iris/Settings.java @@ -4,15 +4,45 @@ public class Settings { public PerformanceSettings performance = new PerformanceSettings(); public GeneratorSettings gen = new GeneratorSettings(); - + public static class PerformanceSettings { - public int threadCount = -1; + public PerformanceMode performanceMode = PerformanceMode.UNLIMITED; + public int threadCount = 4; public int threadPriority = Thread.MAX_PRIORITY; } - + public static class GeneratorSettings { + public double horizontalZoom = 2.125; // 1.856 2.556 + public double heightFracture = 155; + public double heightMultiplier = 1.154; + public double heightExponentBase = 1; + public double heightExponentMultiplier = 1.41; + public double humidityByHeightInfluence = 0.1; + public double temperatureByHeightInfluence = 0.19; + public double temperatureByHeightOffset = 0.25; + public double biomeSoftFracture = 66; + public double biomeSharpFracture = 2; + public double temperatureScale = 1.65; + public double humidityScale = 1.4; + public double heightScale = 1; + public double superHeightScale = 0.65; + public double altBiomeScale = 1; + public double baseHeight = 0.3415; + public double temperatureIgnorance = 1.55; + public double humidityIgnorance = 1.55; + public double heightIgnorance = 1; + public double mountainMultiplier = 1.65; + public double mountainHorizontalZoom = 3.15; + public double mountainSink = 0.0445; + public double superSamplerRadius = 32; + public double superSamplerMultiplier = 1; + public double superSampleOpacity = 1; + public int superSamplerIterations = 9; + public double caveSpread = 3.466; + public double caveChance = 0.03; + public boolean flatBedrock = false; } } diff --git a/src/main/java/ninja/bytecode/iris/atomics/AtomicChunkData.java b/src/main/java/ninja/bytecode/iris/atomics/AtomicChunkData.java index 3626df46d..059fee507 100644 --- a/src/main/java/ninja/bytecode/iris/atomics/AtomicChunkData.java +++ b/src/main/java/ninja/bytecode/iris/atomics/AtomicChunkData.java @@ -13,26 +13,10 @@ import org.bukkit.material.MaterialData; public final class AtomicChunkData implements ChunkGenerator.ChunkData { private static final Field t; - private static final Field[] locks; private static final Field[] sections; private static final int h = 0x1000; private final int maxHeight; - private static ReentrantLock lock0; - private static ReentrantLock lock1; - private static ReentrantLock lock2; - private static ReentrantLock lock3; - private static ReentrantLock lock4; - private static ReentrantLock lock5; - private static ReentrantLock lock6; - private static ReentrantLock lock7; - private static ReentrantLock lock8; - private static ReentrantLock lock9; - private static ReentrantLock lock10; - private static ReentrantLock lock11; - private static ReentrantLock lock12; - private static ReentrantLock lock13; - private static ReentrantLock lock14; - private static ReentrantLock lock15; + private static ReentrantLock[] locks; private char[] s0; private char[] s1; private char[] s2; @@ -180,18 +164,7 @@ public final class AtomicChunkData implements ChunkGenerator.ChunkData return; } - ReentrantLock l = null; - - try - { - l = (ReentrantLock) locks[y >> 4].get(null); - } - - catch(IllegalArgumentException | IllegalAccessException e) - { - e.printStackTrace(); - } - + ReentrantLock l = locks[y >> 4]; l.lock(); getChunkSection(y, true)[(y & 0xf) << 8 | z << 4 | x] = type; l.unlock(); @@ -257,16 +230,15 @@ public final class AtomicChunkData implements ChunkGenerator.ChunkData static { - Field[] l = new Field[16]; + locks = new ReentrantLock[16]; Field[] s = new Field[16]; for(int i = 0; i < 16; i++) { try { - l[i] = AtomicChunkData.class.getDeclaredField("lock" + i); s[i] = AtomicChunkData.class.getDeclaredField("s" + i); - + locks[i] = new ReentrantLock(); } catch(Throwable e) @@ -275,22 +247,8 @@ public final class AtomicChunkData implements ChunkGenerator.ChunkData } } - locks = l; sections = s; - for(int i = 0; i < 16; i++) - { - try - { - locks[i].set(null, new ReentrantLock()); - } - - catch(Throwable e) - { - e.printStackTrace(); - } - } - Field x = null; try diff --git a/src/main/java/ninja/bytecode/iris/gen/GenLayer.java b/src/main/java/ninja/bytecode/iris/gen/GenLayer.java index f2d376795..6d7333772 100644 --- a/src/main/java/ninja/bytecode/iris/gen/GenLayer.java +++ b/src/main/java/ninja/bytecode/iris/gen/GenLayer.java @@ -4,6 +4,7 @@ import java.util.Random; import org.bukkit.World; +import ninja.bytecode.iris.IrisGenerator; import ninja.bytecode.shuriken.math.RNG; public class GenLayer implements IGenLayer @@ -11,12 +12,14 @@ public class GenLayer implements IGenLayer protected RNG rng; protected World world; protected Random random; + protected IrisGenerator iris; - public GenLayer(World world, Random random, RNG rng) + public GenLayer(IrisGenerator iris, World world, Random random, RNG rng) { this.world = world; this.random = random; this.rng = rng; + this.iris = iris; } @Override diff --git a/src/main/java/ninja/bytecode/iris/gen/GenLayerBase.java b/src/main/java/ninja/bytecode/iris/gen/GenLayerBase.java index b63a5fa2c..6584b18e6 100644 --- a/src/main/java/ninja/bytecode/iris/gen/GenLayerBase.java +++ b/src/main/java/ninja/bytecode/iris/gen/GenLayerBase.java @@ -4,7 +4,10 @@ import java.util.Random; import org.bukkit.World; +import ninja.bytecode.iris.Iris; +import ninja.bytecode.iris.IrisGenerator; import ninja.bytecode.shuriken.math.CNG; +import ninja.bytecode.shuriken.math.M; import ninja.bytecode.shuriken.math.RNG; public class GenLayerBase extends GenLayer @@ -12,25 +15,41 @@ public class GenLayerBase extends GenLayer private double[][][] scatterCache; private CNG gen; private CNG fracture; + private CNG hfracture; + private CNG height; + private CNG superheight; - public GenLayerBase(World world, Random random, RNG rng) + public GenLayerBase(IrisGenerator iris, World world, Random random, RNG rng) { //@builder - super(world, random, rng); + super(iris, world, random, rng); scatterCache = new double[16][][]; CNG scatter = new CNG(rng.nextRNG(), 1, 1) - .scale(10); + .scale(10); + hfracture = new CNG(rng.nextRNG(), 1, 2) + .scale(0.0124); gen = new CNG(rng.nextRNG(), 0.19D, 16) - .scale(0.012) - .amp(0.5) - .freq(1.1) - .fractureWith(new CNG(rng.nextRNG(), 1, 6) - .scale(0.018) - .injectWith(CNG.MULTIPLY) - .child(new CNG(rng.nextRNG(), 0.745, 2) - .scale(0.1)) - .fractureWith(new CNG(rng.nextRNG(), 1, 3) - .scale(0.15), 24), 44); + .scale(0.012) + .amp(0.5) + .freq(1.1) + .fractureWith(new CNG(rng.nextRNG(), 1, 6) + .scale(0.018) + .injectWith(CNG.MULTIPLY) + .child(new CNG(rng.nextRNG(), 0.745, 2) + .scale(0.1)) + .fractureWith(new CNG(rng.nextRNG(), 1, 3) + .scale(0.15), 24), 44); + height = new CNG(rng.nextRNG(), 1, 16) + .scale(0.0017601 * Iris.settings.gen.heightScale) + .fractureWith(new CNG(rng.nextRNG(), 1, 6) + .scale(0.0174) + .fractureWith(new CNG(rng.nextRNG(), 1, 1) + .scale(0.0034), 31) + .scale(0.066), 58); + superheight = new CNG(rng.nextRNG(), 1, 6) + .scale(0.0025 * Iris.settings.gen.superHeightScale) + .fractureWith(new CNG(rng.nextRNG(), 1, 1) + .scale(0.021), 250); fracture = new CNG(rng.nextRNG(), 0.6D, 4) .scale(0.118); //@done @@ -51,6 +70,11 @@ public class GenLayerBase extends GenLayer } } + public double getHeight(double x, double z) + { + return M.clip(Math.pow(height.noise(x + (hfracture.noise(x, z) * Iris.settings.gen.heightFracture), z + (hfracture.noise(z, x) * Iris.settings.gen.heightFracture)), Iris.settings.gen.heightExponentBase + (superheight.noise(x, z) * Iris.settings.gen.heightExponentMultiplier)) * Iris.settings.gen.heightMultiplier, 0D, 1D); + } + public int scatterInt(int x, int y, int z, int bound) { return (int) (scatter(x, y, z) * (double) (bound - 1)); @@ -67,11 +91,12 @@ public class GenLayerBase extends GenLayer } @Override - public double generateLayer(double noise, double dx, double dz) + public double generateLayer(double gnoise, double dx, double dz) { + double noise = gnoise + getHeight(dx, dz); double fnoise = fracture.noise(dx, dz); dx += (fnoise * 44); dz -= (fnoise * 44); - return ((noise * 0.5) + (gen.noise(dx, dz) * (0.15 + (noise * 0.65)))) + 0.31; + return ((noise * 0.185) + (gen.noise(dx, dz) * (0.15 + (noise * 0.65)))); } } diff --git a/src/main/java/ninja/bytecode/iris/gen/GenLayerBiome.java b/src/main/java/ninja/bytecode/iris/gen/GenLayerBiome.java deleted file mode 100644 index 7a47a88f7..000000000 --- a/src/main/java/ninja/bytecode/iris/gen/GenLayerBiome.java +++ /dev/null @@ -1,97 +0,0 @@ -package ninja.bytecode.iris.gen; - -import java.util.Random; - -import org.bukkit.World; - -import ninja.bytecode.iris.util.RealBiome; -import ninja.bytecode.shuriken.math.CNG; -import ninja.bytecode.shuriken.math.M; -import ninja.bytecode.shuriken.math.RNG; - -public class GenLayerBiome extends GenLayer -{ - private CNG temperature; - private CNG humidity; - private CNG hfracture; - private CNG alt; - private CNG bfracture; - private CNG height; - private CNG superheight; - - public GenLayerBiome(World world, Random random, RNG rng) - { - super(world, random, rng); - //@builder - temperature = new CNG(rng.nextRNG(), 1, 2) - .scale(0.0022) - .fractureWith(new CNG(rng.nextRNG(), 1, 1).scale(0.06), 32); - hfracture = new CNG(rng.nextRNG(), 1, 2) - .scale(0.0124); - humidity = new CNG(rng.nextRNG(), 1, 2) - .scale(0.0024) - .fractureWith(new CNG(rng.nextRNG(), 1, 1).scale(0.06), 32); - bfracture = new CNG(rng.nextRNG(), 1, 1) - .scale(0.524); - superheight = new CNG(rng.nextRNG(), 1, 8) - .scale(0.0004) - .fractureWith(new CNG(rng.nextRNG(), 1, 1).scale(0.021), 250); - height = new CNG(rng.nextRNG(), 1, 8) - .scale(0.0017601) - .fractureWith(new CNG(rng.nextRNG(), 1, 6) - .scale(0.0174) - .fractureWith(new CNG(rng.nextRNG(), 1, 1) - .scale(0.0034), 31) - .scale(0.066), 58); - alt = new CNG(rng.nextRNG(), 1, 1) - .scale(0.0008) - .fractureWith(new CNG(rng.nextRNG(), 1, 1).scale(0.3), 100); - //@done - } - - public RealBiome getBiome(double x, double z) - { - return RealBiome.match(getTemperature(x, z) * 2, getHumidity(x, z), getHeight(x, z), getAlt(x, z)); - } - - public RealBiome getBiome(double x, double z, double temp, double height) - { - return RealBiome.match(temp * 2, getHumidity(x, z), height, getAlt(x, z)); - } - - private double getAlt(double x, double z) - { - return alt.noise(x, z); - } - - public double getTemperature(double x, double z) - { - return getTemperature(x, z, getHeight(x, z)); - } - - public double getTemperature(double x, double z, double height) - { - return M.clip(temperature.noise(x, z) - (height * 0.19), 0D, 1D); - } - - public double getBFracture(double x, double z) - { - return bfracture.noise(x, z); - } - - public double getHumidity(double x, double z) - { - return humidity.noise(x, z); - } - - public double getHeight(double x, double z) - { - return M.clip(Math.pow(height.noise(x + (hfracture.noise(x, z) * 33), z + (hfracture.noise(z, x) * 33)), 2.31 + superheight.noise(x, z)) * 2.654, 0D, 1D); - } - - @Override - public double generateLayer(double noise, double dx, double dz) - { - return getHeight(dx, dz); - } -} diff --git a/src/main/java/ninja/bytecode/iris/gen/GenLayerDeepOcean.java b/src/main/java/ninja/bytecode/iris/gen/GenLayerDeepOcean.java index ccdd53d21..5b75a179e 100644 --- a/src/main/java/ninja/bytecode/iris/gen/GenLayerDeepOcean.java +++ b/src/main/java/ninja/bytecode/iris/gen/GenLayerDeepOcean.java @@ -4,6 +4,7 @@ import java.util.Random; import org.bukkit.World; +import ninja.bytecode.iris.IrisGenerator; import ninja.bytecode.shuriken.math.CNG; import ninja.bytecode.shuriken.math.RNG; @@ -13,10 +14,10 @@ public class GenLayerDeepOcean extends GenLayer private CNG cond; private double deepHeight = 0.493; - public GenLayerDeepOcean(World world, Random random, RNG rng) + public GenLayerDeepOcean(IrisGenerator iris, World world, Random random, RNG rng) { //@builder - super(world, random, rng); + super(iris, world, random, rng); gen = new CNG(rng.nextRNG(), 1D, 4) .scale(0.023) .fractureWith(new CNG(rng.nextRNG(), 1D, 1) diff --git a/src/main/java/ninja/bytecode/iris/gen/GenLayerFracture.java b/src/main/java/ninja/bytecode/iris/gen/GenLayerFracture.java new file mode 100644 index 000000000..8bd2ab007 --- /dev/null +++ b/src/main/java/ninja/bytecode/iris/gen/GenLayerFracture.java @@ -0,0 +1,48 @@ +package ninja.bytecode.iris.gen; + +import java.util.Random; + +import org.bukkit.World; + +import ninja.bytecode.iris.IrisGenerator; +import ninja.bytecode.shuriken.math.CNG; +import ninja.bytecode.shuriken.math.M; +import ninja.bytecode.shuriken.math.RNG; + +public class GenLayerFracture extends GenLayer +{ + private CNG gen; + private CNG cond; + private double shootHeight = 0.563; + + public GenLayerFracture(IrisGenerator iris, World world, Random random, RNG rng) + { + //@builder + super(iris, world, random, rng); + gen = new CNG(rng.nextRNG(), 1D, 4) + .scale(0.013) + .fractureWith(new CNG(rng.nextRNG(), 1D, 1) + .scale(0.05), 25); + cond = new CNG(rng.nextRNG(), 1D, 4) + .scale(0.018) + .fractureWith(new CNG(rng.nextRNG(), 1D, 1) + .scale(0.025), 9); + //@done + } + + @Override + public double generateLayer(double noise, double dx, double dz) + { + double shootHeight = this.shootHeight + (cond.noise(dx, dz) * 0.035); + + if(noise >= shootHeight) + { + double multiplier = M.rangeScale(0, 0.055, this.shootHeight, 1D, cond.noise(-dx, -dz)); + double on = gen.noise(dx, dz) * multiplier; + + return noise + on; + } + + return noise; + } +} diff --git a/src/main/java/ninja/bytecode/iris/gen/GenLayerMountains.java b/src/main/java/ninja/bytecode/iris/gen/GenLayerMountains.java new file mode 100644 index 000000000..5293c174a --- /dev/null +++ b/src/main/java/ninja/bytecode/iris/gen/GenLayerMountains.java @@ -0,0 +1,36 @@ +package ninja.bytecode.iris.gen; + +import java.util.Random; + +import org.bukkit.World; + +import ninja.bytecode.iris.Iris; +import ninja.bytecode.iris.IrisGenerator; +import ninja.bytecode.shuriken.math.CNG; +import ninja.bytecode.shuriken.math.RNG; + +public class GenLayerMountains extends GenLayer +{ + private CNG gen; + + public GenLayerMountains(IrisGenerator iris, World world, Random random, RNG rng) + { + //@builder + super(iris, world, random, rng); + gen = new CNG(rng.nextRNG(), 1D, 2) + .scale(0.0011 * Iris.settings.gen.mountainHorizontalZoom) + .child(new CNG(rng.nextRNG(), 1D, 3).scale(0.00012 * Iris.settings.gen.mountainHorizontalZoom)) + .child(new CNG(rng.nextRNG(), 1D, 4).scale(0.00014 * Iris.settings.gen.mountainHorizontalZoom)) + .child(new CNG(rng.nextRNG(), 1D, 5).scale(0.00015 * Iris.settings.gen.mountainHorizontalZoom)) + .injectWith(CNG.MULTIPLY) + .fractureWith(new CNG(rng.nextRNG(), 1D, 1) + .scale(0.05), 25); + //@done + } + + @Override + public double generateLayer(double noise, double dx, double dz) + { + return noise + (gen.noise(dx, dz) - Iris.settings.gen.mountainSink) * Iris.settings.gen.mountainMultiplier; + } +} diff --git a/src/main/java/ninja/bytecode/iris/gen/GenLayerSuperSample.java b/src/main/java/ninja/bytecode/iris/gen/GenLayerSuperSample.java new file mode 100644 index 000000000..eedb0d27d --- /dev/null +++ b/src/main/java/ninja/bytecode/iris/gen/GenLayerSuperSample.java @@ -0,0 +1,81 @@ +package ninja.bytecode.iris.gen; + +import java.util.Random; + +import org.bukkit.World; + +import ninja.bytecode.iris.Iris; +import ninja.bytecode.iris.IrisGenerator; +import ninja.bytecode.shuriken.math.CNG; +import ninja.bytecode.shuriken.math.M; +import ninja.bytecode.shuriken.math.RNG; + +public class GenLayerSuperSample extends GenLayer +{ + private CNG gen; + private CNG radius; + + public GenLayerSuperSample(IrisGenerator iris, World world, Random random, RNG rng) + { + //@builder + super(iris, world, random, rng); + gen = new CNG(rng.nextRNG(), 1D, 4) + .scale(0.02 * Iris.settings.gen.superSamplerMultiplier); + radius = new CNG(rng.nextRNG(), 1D, 2) + .scale(0.01); + //@done + } + + public double getSuperSampledHeight(double dx, double dz) + { + double ssf = 0; + double height = iris.getRawHeight(dx, dz); + + if(Iris.settings.gen.superSamplerIterations == 0) + { + return height; + } + + double t = 0; + double sig = Iris.settings.gen.superSampleOpacity * radius.noise(dx, dz); + + for(int i = 0; i < Iris.settings.gen.superSamplerIterations; i++) + { + //@builder + double ss = 0; + double mul = Iris.settings.gen.superSamplerRadius; + double[] ssv = new double[] { + getRawHeight(dx, dz, Math.toRadians(getAngle(dx, dz)), mul / (double)(i + 1), true), + getRawHeight(dx, dz, Math.toRadians(getAngle(dx, dz)), mul / (double)(i + 1), false) + }; + //@done + for(double j : ssv) + { + ss += j; + } + + t += (double) (1D / (i + 1)); + ssf += (ss / 2D) / (double) (i + 1); + } + + return (height * (1D - sig)) + ((ssf / t) * sig); + } + + public double getRawHeight(double dx, double dz, double rad, double mult, boolean a) + { + double dax = dx + ((Math.sin(rad) * mult) * (a ? 1 : 1)); + double daz = dz + ((Math.cos(rad) * mult) * (a ? -1 : -1)); + return iris.getRawHeight(dax, daz); + } + + public double getAngle(double x, double z) + { + return M.percentRange(gen.noise(x, z), 0, 365); + } + + @Override + public double generateLayer(double noise, double dx, double dz) + { + return noise; + } +} diff --git a/src/main/java/ninja/bytecode/iris/util/Catalyst12.java b/src/main/java/ninja/bytecode/iris/util/Catalyst12.java new file mode 100644 index 000000000..d84cd19fe --- /dev/null +++ b/src/main/java/ninja/bytecode/iris/util/Catalyst12.java @@ -0,0 +1,54 @@ +package ninja.bytecode.iris.util; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_12_R1.CraftWorld; + +import net.minecraft.server.v1_12_R1.IBlockData; +import ninja.bytecode.iris.Iris; +import ninja.bytecode.iris.MB; +import ninja.bytecode.shuriken.execution.J; + +public class Catalyst12 +{ + public static void waitForChunk(World w, int x, int z) + { + if(!w.isChunkLoaded(x, z)) + { + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Iris.instance, () -> w.loadChunk(x, z, true)); + } + + int i = 0; + while(!w.isChunkLoaded(x, z) && i < 20) + { + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Iris.instance, () -> w.loadChunk(x, z, true)); + J.sleep(50); + i++; + } + } + + @SuppressWarnings("deprecation") + public static void setBlock(Location l, MB m) + { + int x = l.getBlockX(); + int y = l.getBlockY(); + int z = l.getBlockZ(); + net.minecraft.server.v1_12_R1.World w = ((CraftWorld) l.getWorld()).getHandle(); + net.minecraft.server.v1_12_R1.Chunk chunk = w.getChunkAt(x >> 4, z >> 4); + int combined = m.material.getId() + (m.data << 12); + IBlockData ibd = net.minecraft.server.v1_12_R1.Block.getByCombinedId(combined); + + if(chunk.getSections()[y >> 4] == null) + { + chunk.getSections()[y >> 4] = new net.minecraft.server.v1_12_R1.ChunkSection(y >> 4 << 4, chunk.world.worldProvider.m()); + } + + net.minecraft.server.v1_12_R1.ChunkSection sec = chunk.getSections()[y >> 4]; + + synchronized(sec) + { + sec.setType(x & 15, y & 15, z & 15, ibd); + } + } +} \ No newline at end of file diff --git a/src/main/java/ninja/bytecode/iris/util/RealBiome.java b/src/main/java/ninja/bytecode/iris/util/RealBiome.java index 1561ba986..12dbf887f 100644 --- a/src/main/java/ninja/bytecode/iris/util/RealBiome.java +++ b/src/main/java/ninja/bytecode/iris/util/RealBiome.java @@ -3,318 +3,43 @@ package ninja.bytecode.iris.util; import org.bukkit.Material; import org.bukkit.block.Biome; +import net.minecraft.server.v1_12_R1.BiomeBase; +import net.minecraft.server.v1_12_R1.Block; +import net.minecraft.server.v1_12_R1.IBlockData; import ninja.bytecode.iris.MB; -import ninja.bytecode.iris.gen.GenLayerBase; -import ninja.bytecode.shuriken.collections.GList; +import ninja.bytecode.shuriken.format.F; public class RealBiome { - public static final double a = 0; - public static final double h = 0.5; - public static final double t = 0.5; - - //@builder - public static final RealBiome[] biomes = { - new RealBiome(0, 0.5, h, -1).water(), // Ocean - new RealBiome(1, 0.6, 0.4, 0.125), // Plains - new RealBiome(2, 2, 0, 0.125) // Desert - .surface(new MB(Material.SAND)) - .dirt(new MB(Material.SAND), new MB(Material.SAND, 1)) - .rock(new MB(Material.SANDSTONE)), - new RealBiome(3, 0.2, 0.3, 0.56), // Extreme Hills - new RealBiome(4, 0.5, 0.8, a), // Forest - new RealBiome(5, 0.25, 0.8, 0.2), // Taiga - new RealBiome(6, 0.8, 0.9, -0.2), // Swampland - new RealBiome(7, t, h, -0.5).river(), // River - new RealBiome(8, 2, 0, a).dimensional(), // Hell - new RealBiome(9, t, h, a).dimensional(), // The End - new RealBiome(10, 0, 0.5, -1).water(), // Frozen Ocean - new RealBiome(11, 0, 0.5, -0.5).river(), // Frozen River - new RealBiome(12, 0, 0.5, 0.125).surface(new MB(Material.SNOW_BLOCK)), // Ice Plains - new RealBiome(13, 0, 0.5, 0.765) // Ice Mountains - .surface(new MB(Material.SNOW_BLOCK)) - .dirt(new MB(Material.PACKED_ICE)), - new RealBiome(14, 0.9, 1, 0.2).modifier() // Mushroom Island - .surface(new MB(Material.MYCEL)), - new RealBiome(15, 0, 1, 0).modifier() // Mushroom Island Shore - .surface(new MB(Material.MYCEL)), - new RealBiome(16, 0.8, 0.4, 0).beach(), // Beaches - new RealBiome(17, 2, 0, 0.75) // Desert Hills - .surface(new MB(Material.SAND)) - .dirt(new MB(Material.SAND), new MB(Material.SAND, 1)) - .rock(new MB(Material.SANDSTONE)), - new RealBiome(18, 0.6, 0.8, 0.75), // Forest Hills - new RealBiome(19, 0.25, 0.8, 0.75), // Taiga Hills - new RealBiome(20, 0.2, 0.3, 0.8), // Extreme Hills Edge - new RealBiome(21, 0.95, 0.9, a), // Jungle - new RealBiome(22, 0.95, 0.9, 0.75), // Jungle - new RealBiome(23, 0.9, 0.9, 0.15), // Jungle Edge - new RealBiome(24, t, h, -1.8).water(), // Deep Ocean - new RealBiome(25, 0.2, 0.3, 0.1).beach(), // Stone Beach - new RealBiome(26, 0.2, 0.3, 0).beach(), // Cold Beach - new RealBiome(27, 0.5, 0.5, a), // Birch Forest - new RealBiome(28, 0.4, 0.4, 0.25), // Birch Forest Hills - new RealBiome(29, 0.7, 0.8, a), // Roofed Forest - new RealBiome(30, -0.5, 0.4, 0.2), // Cold Taiga - new RealBiome(31, -0.5, 0.4, 0.75), // Cold Taiga Hills - new RealBiome(32, 0.4, 0.8, 0.2), // Redwood Taiga - new RealBiome(33, 0.3, 0.8, 0.75), // Redwood Taiga Hills - new RealBiome(34, 0.2, 0.3, 1), // Extra Hills with Trees - new RealBiome(35, 1.2, 0, 0.125), // Savanna - new RealBiome(36, 1, 0, 0.28), // Savanna Plateau - new RealBiome(37, 2, 0, a), // Mesa - new RealBiome(38, 2, 0, 0.28), // Mesa Plateau F - new RealBiome(39, 2, 0, 0.31), // Mesa Plateau - }; - //@done - - private int biomeId; + private Biome b; private double temperature; - private double humidity; private double height; - private boolean modifier; - private boolean river; - private boolean water; - private boolean beach; - private boolean dimensional; - private GList surface; - private GList dirt; - private GList rock; - private boolean defs; - private boolean defd; - private boolean defr; + private double humidity; + private MB surface; + private MB dirt; - public RealBiome(int biomeId, double temperature, double humidity, double height) + public RealBiome(Biome b) { - defs = true; - defd = true; - defr = true; - this.biomeId = biomeId; - this.temperature = temperature; - this.humidity = humidity; - this.height = height; - surface = new GList<>(); - dirt = new GList<>(); - rock = new GList<>(); - surface.add(new MB(Material.GRASS)); - dirt.add(new MB(Material.DIRT), new MB(Material.DIRT, 1)); - rock.add(new MB(Material.STONE), new MB(Material.STONE, 5), new MB(Material.COBBLESTONE)); - temperature = temperature > 1 ? 1 : temperature < 0 ? 0 : temperature; - humidity = humidity > 1 ? 1 : humidity < 0 ? 0 : humidity; - height = height > 1 ? 1 : height < 0 ? 0 : height; + this.b = b; + BiomeBase base = BiomeBase.a(b.ordinal()); + surface = toMB(base.q); + dirt = toMB(base.r); + temperature = base.getTemperature(); + humidity = base.getHumidity(); + height = base.j(); } - public static RealBiome match(double temperature, double humidity, double height, double d) + public String toString() { - GList b = new GList<>(); - double distance = Double.MAX_VALUE; - - for(RealBiome i : biomes) - { - if(i.modifier) - { - continue; - } - - double dist = i.getDistance(temperature, humidity, height); - if(dist < distance) - { - distance = dist; - b.add(i); - } - } - - return b.get((int) (d * Math.min(b.size(), 3))); + return F.capitalizeWords(b.toString().toLowerCase().replaceAll("\\Q_\\E", " ")) + " Temp: " + temperature + " Humidity: " + humidity + " Height: " + height + " Surf: " + F.capitalizeWords(surface.material.toString().replaceAll("_", " ").toLowerCase())+ " Dirt: " + F.capitalizeWords(dirt.material.toString().replaceAll("_", " ").toLowerCase()); } - public double getDistance(double temperature, double humidity, double height) + @SuppressWarnings("deprecation") + public MB toMB(IBlockData d) { - return Math.abs((temperature - this.temperature) * 3.5) + Math.abs((humidity - this.humidity) * 2.5) + Math.abs((height - this.height) * 4.8); - } - - public Biome getBiome() - { - return Biome.values()[biomeId]; - } - - public RealBiome surface(MB... mb) - { - if(defs) - { - defs = false; - surface.clear(); - } - this.surface.add(mb); - return this; - } - - public RealBiome dirt(MB... mb) - { - if(defd) - { - defd = false; - dirt.clear(); - } - - this.dirt.add(mb); - return this; - } - - public RealBiome rock(MB... mb) - { - if(defr) - { - defr = false; - rock.clear(); - } - - this.rock.add(mb); - return this; - } - - public RealBiome modifier() - { - modifier = true; - return this; - } - - public RealBiome river() - { - river = true; - return this.modifier(); - } - - public RealBiome water() - { - water = true; - return this.modifier(); - } - - public RealBiome beach() - { - beach = true; - return this.modifier(); - } - - public RealBiome dimensional() - { - dimensional = true; - return this.modifier(); - } - - public MB surface(int x, int y, int z, GenLayerBase glBase) - { - return surface.get(glBase.scatterInt(x, y, z, surface.size())); - } - - public MB dirt(int x, int y, int z, GenLayerBase glBase) - { - return dirt.get(glBase.scatterInt(x, y, z, dirt.size())); - } - - public MB rock(int x, int y, int z, GenLayerBase glBase) - { - return rock.get(glBase.scatterInt(x, y, z, rock.size())); - } - - public static double getA() - { - return a; - } - - public static double getH() - { - return h; - } - - public static double getT() - { - return t; - } - - public static RealBiome[] getBiomes() - { - return biomes; - } - - public int getBiomeId() - { - return biomeId; - } - - public double getTemperature() - { - return temperature; - } - - public double getHumidity() - { - return humidity; - } - - public double getHeight() - { - return height; - } - - public boolean isModifier() - { - return modifier; - } - - public boolean isRiver() - { - return river; - } - - public boolean isWater() - { - return water; - } - - public boolean isBeach() - { - return beach; - } - - public boolean isDimensional() - { - return dimensional; - } - - public GList getSurface() - { - return surface; - } - - public GList getDirt() - { - return dirt; - } - - public GList getRock() - { - return rock; - } - - public boolean isDefs() - { - return defs; - } - - public boolean isDefd() - { - return defd; - } - - public boolean isDefr() - { - return defr; - } - - public MB getSurfaceDecoration() - { - // TODO Auto-generated method stub - return null; + int i = Block.getCombinedId(d); + int j = i & 4095; + int k = i >> 12 & 15; + return new MB(Material.getMaterial(j), k); } } \ No newline at end of file diff --git a/voronoi.png b/voronoi.png new file mode 100644 index 0000000000000000000000000000000000000000..6f253b4e6a6d09dce271e3852a9f7c989554bfa3 GIT binary patch literal 24186 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