package com.volmit.iris.gen; import org.bukkit.Material; import org.bukkit.block.Biome; import org.bukkit.block.data.Bisected; import org.bukkit.block.data.Bisected.Half; import com.volmit.iris.Iris; import com.volmit.iris.gen.atomics.AtomicSliver; import com.volmit.iris.gen.atomics.AtomicSliverMap; import com.volmit.iris.gen.layer.GenLayerBiome; import com.volmit.iris.gen.layer.GenLayerCarve; import com.volmit.iris.gen.layer.GenLayerCave; import com.volmit.iris.gen.layer.GenLayerRavine; import com.volmit.iris.gen.scaffold.GeneratedChunk; import com.volmit.iris.gen.scaffold.TerrainChunk; import com.volmit.iris.gen.scaffold.TerrainTarget; import com.volmit.iris.noise.CNG; import com.volmit.iris.object.DecorationPart; import com.volmit.iris.object.InferredType; import com.volmit.iris.object.IrisBiome; import com.volmit.iris.object.IrisDecorator; import com.volmit.iris.object.IrisCaveFluid; import com.volmit.iris.object.IrisDepositGenerator; import com.volmit.iris.object.IrisDimension; import com.volmit.iris.object.IrisGenerator; import com.volmit.iris.object.IrisRegion; import com.volmit.iris.object.IrisShapedGeneratorStyle; import com.volmit.iris.util.B; import com.volmit.iris.util.BiomeMap; import com.volmit.iris.util.CaveResult; import com.volmit.iris.util.ChronoLatch; import com.volmit.iris.util.FastBlockData; import com.volmit.iris.util.HeightMap; import com.volmit.iris.util.IrisLock; import com.volmit.iris.util.KList; import com.volmit.iris.util.KMap; import com.volmit.iris.util.M; import com.volmit.iris.util.PrecisionStopwatch; import com.volmit.iris.util.RNG; import lombok.Data; import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = false) public abstract class TopographicTerrainProvider extends ParallelTerrainProvider { private long lastUpdateRequest = M.ms(); private long lastChunkLoad = M.ms(); private GenLayerCave glCave; private GenLayerCarve glCarve; private GenLayerBiome glBiome; private GenLayerRavine glRavine; private RNG rockRandom; private IrisLock regionLock; private KMap generators; private KList generatorList; private CNG masterFracture; private int generatorSize; private ChronoLatch cwarn = new ChronoLatch(1000); public TopographicTerrainProvider(TerrainTarget t, String dimensionName, int threads) { super(t, dimensionName, threads); setGeneratorSize(0); setGenerators(new KMap<>()); setGeneratorList(new KList<>()); setRegionLock(new IrisLock("BiomeChunkGenerator")); } @Override public void onInit(RNG rng) { generatorSize = 0; super.onInit(rng); getData().preferFolder(getDimension().getLoadFile().getParentFile().getParentFile().getName()); buildGenLayers(getMasterRandom()); loadGenerators(); } private void buildGenLayers(RNG rng) { setGlBiome(new GenLayerBiome(this, rng.nextParallelRNG(24671))); setMasterFracture(CNG.signature(rng.nextParallelRNG(13)).scale(0.12)); setRockRandom(getMasterRandom().nextParallelRNG(2858678)); setGlCave(new GenLayerCave(this, rng.nextParallelRNG(238948))); setGlCarve(new GenLayerCarve(this, rng.nextParallelRNG(968346576))); setGlRavine(new GenLayerRavine(this, rng.nextParallelRNG(-229354923))); } public int getCarvedHeight(int x, int z, boolean ignoreFluid) { if(ignoreFluid) { return getCache().getCarvedHeightIgnoreWater(x, z); } return getCache().getCarvedHeight(x, z); } public int getCarvedHeight(int x, int z) { return getCarvedHeight(x, z, false); } public int getCarvedWaterHeight(int x, int z) { return getCarvedHeight(x, z, true); } public KList getCaves(int x, int z) { return glCave.genCaves(x, z, x & 15, z & 15, null); } @Override protected void onPreGenerate(RNG random, int x, int z, TerrainChunk terrain, HeightMap height, BiomeMap biomeMap, AtomicSliverMap map) { } @Override protected int onGenerateColumn(int cx, int cz, int rx, int rz, int x, int z, AtomicSliver sliver, BiomeMap biomeMap, boolean sampled) { if(x > 15 || x < 0 || z > 15 || z < 0) { throw new RuntimeException("Invalid OnGenerate call: x:" + x + " z:" + z); } RNG crand = getMasterRandom().nextParallelRNG(rx).nextParallelRNG(rz); FastBlockData block; int fluidHeight = getDimension().getFluidHeight(); double ox = getModifiedX(rx, rz); double oz = getModifiedZ(rx, rz); double wx = getZoomed(ox); double wz = getZoomed(oz); int depth = 0; double noise = getTerrainHeight(rx, rz); int height = (int) Math.round(noise); boolean carvable = getGlCarve().couldCarveBelow(height); IrisRegion region = sampleRegion(rx, rz); IrisBiome biome = sampleTrueBiome(rx, rz); IrisBiome carveBiome = null; Biome onlyBiome = Iris.biome3d ? null : biome.getGroundBiome(getMasterRandom(), rz, getDimension().getFluidHeight(), rx); IrisCaveFluid forceFluid = getDimension().getForceFluid().hasFluid(getData()) ? getDimension().getForceFluid() : null; if(biome == null) { throw new RuntimeException("Null Biome!"); } KList layers = biome.generateLayers(rx, rz, getMasterRandom(), height, height - getFluidHeight(), getData()); KList cavernLayers = null; KList seaLayers = biome.isAquatic() || biome.isShore() ? biome.generateSeaLayers(rx, rz, getMasterRandom(), fluidHeight - height, getData()) : new KList<>(); FastBlockData biomeFluid = biome.getFluidType().isEmpty() ? null : B.get(biome.getFluidType()); boolean caverning = false; KList cavernHeights = new KList<>(); int lastCavernHeight = -1; boolean biomeAssigned = false; int max = Math.max(height, fluidHeight); int biomeMax = Math.min(max + 32, 255); // From Height to Bedrock for(int k = max; k >= 0; k--) { boolean cavernSurface = false; boolean bedrock = k == 0; boolean underwater = k > height && k <= fluidHeight; // Bedrock if(bedrock && getDimension().isBedrock()) { if(biomeMap != null) { sliver.set(k, biome.getDerivative()); } sliver.set(k, BEDROCK); continue; } // Carving if(carvable && getGlCarve().isCarved(rx, k, rz)) { if(biomeMap != null) { if(carveBiome == null) { carveBiome = biome.getRealCarvingBiome(getData()); } sliver.set(k, carveBiome.getDerivative()); } sliver.set(k, CAVE_AIR); caverning = true; continue; } // Carved Surface else if(carvable && caverning) { lastCavernHeight = k; cavernSurface = true; cavernHeights.add(k); caverning = false; } // Set Biome if(!biomeAssigned && biomeMap != null && k < max) { biomeAssigned = true; if(Iris.biome3d) { sliver.set(k, biome.getGroundBiome(getMasterRandom(), rz, k, rx)); for(int kv = max; kv <= biomeMax; kv++) { sliver.set(kv, biome.getSkyBiome(getMasterRandom(), rz, kv, rx)); } } else { sliver.set(getFluidHeight(), onlyBiome); } biomeMap.setBiome(x, z, biome); } if(Iris.biome3d && k <= Math.max(height, fluidHeight)) { sliver.set(k, biome.getGroundBiome(getMasterRandom(), rz, k, rx)); } // Set Sea Material (water/lava) if(underwater) { block = seaLayers.hasIndex(fluidHeight - k) ? seaLayers.get(depth) : biomeFluid != null ? biomeFluid : getDimension().getFluidPalette().get(rockRandom, wx, k, wz, getData()); } // Set Surface Material for cavern layer surfaces else if(carvable && cavernHeights.isNotEmpty() && lastCavernHeight - k >= 0 && lastCavernHeight - k < 5) { if(carveBiome == null) { carveBiome = biome.getRealCarvingBiome(getData()); } if(cavernLayers == null) { cavernLayers = carveBiome.generateLayers(rx, rz, getMasterRandom(), 5, height - getFluidHeight(), getData()); } block = cavernLayers.hasIndex(lastCavernHeight - k) ? cavernLayers.get(lastCavernHeight - k) : cavernLayers.get(0); } // Set Surface Material for true surface else { block = layers.hasIndex(depth) ? layers.get(depth) : getDimension().getRockPalette().get(rockRandom, wx, k, wz, getData()); depth++; } // Set block and update heightmaps sliver.set(k, block); // Decorate underwater surface if(!cavernSurface && (k == height && B.isSolid(block.getMaterial()) && k < fluidHeight)) { decorateUnderwater(crand, biome, sliver, k, rx, rz); } // Decorate Cavern surfaces, but not the true surface if((carvable && cavernSurface) && !(k == Math.max(height, fluidHeight) && block.getMaterial().isSolid() && k < 255 && k >= fluidHeight)) { if(carveBiome == null) { carveBiome = biome.getRealCarvingBiome(getData()); } decorateLand(crand, carveBiome, sliver, k, rx, rz, block); } if(forceFluid != null && B.isAir(block) && (forceFluid.isInverseHeight() ? k >= forceFluid.getFluidHeight() : k <= forceFluid.getFluidHeight())) { sliver.set(k, forceFluid.getFluid(getData())); } } // Carve out biomes KList caveResults = glCave.genCaves(rx, rz, x, z, sliver); IrisBiome caveBiome = glBiome.generateData(InferredType.CAVE, wx, wz, rx, rz, region); // Decorate Cave Biome Height Sections if(caveBiome != null) { for(CaveResult i : caveResults) { if(i.getFloor() < 0 || i.getFloor() > 255 || i.getCeiling() > 255 || i.getCeiling() < 0) { continue; } if(Iris.biome3d) { for(int j = i.getFloor(); j <= i.getCeiling(); j++) { sliver.set(j, caveBiome.getGroundBiome(getMasterRandom(), rz, j, rx)); } } KList floor = caveBiome.generateLayers(wx, wz, rockRandom, i.getFloor() - 2, i.getFloor() - 2, getData()); KList ceiling = caveBiome.generateLayers(wx + 256, wz + 256, rockRandom, (carvable ? getCarvedWaterHeight(rx, rz) : height) - i.getCeiling() - 2, (carvable ? getCarvedWaterHeight(rx, rz) : height) - i.getCeiling() - 2, getData()); FastBlockData blockc = null; for(int j = 0; j < floor.size(); j++) { if(j == 0) { blockc = floor.get(j); } sliver.set(i.getFloor() - j, floor.get(j)); } for(int j = ceiling.size() - 1; j > 0; j--) { sliver.set(i.getCeiling() + j, ceiling.get(j)); } if(blockc != null && !sliver.isSolid(i.getFloor() + 1)) { decorateCave(crand, caveBiome, sliver, i.getFloor(), rx, rz, blockc); } } } if(Math.max(height, fluidHeight) > 255) { return height; } block = sliver.get(Math.max(height, fluidHeight)); // Decorate True Surface if(block.getMaterial().isSolid()) { decorateLand(crand, biome, sliver, Math.max(height, fluidHeight), rx, rz, block); } return height; } @Override protected GeneratedChunk onGenerate(RNG random, int x, int z, TerrainChunk terrain) { GeneratedChunk map = super.onGenerate(random, x, z, terrain); if(!getDimension().isVanillaCaves()) { generateDeposits(random.nextParallelRNG(x * ((z * 39) + 10000)).nextParallelRNG(z + z - x), terrain, x, z); } return map; } private void decorateLand(RNG rng, IrisBiome biome, AtomicSliver sliver, int k, int rx, int rz, FastBlockData block) { if(!getDimension().isDecorate()) { return; } int j = 0; for(IrisDecorator i : biome.getDecorators()) { if(i.getPartOf().equals(DecorationPart.SHORE_LINE) && (!touchesSea(rx, rz) || k != getFluidHeight())) { continue; } FastBlockData d = i.getBlockData(biome, rng.nextParallelRNG(38888 + biome.getRarity() + biome.getName().length() + j++), rx, rz, getData()); if(d != null) { if(!B.canPlaceOnto(d.getMaterial(), block.getMaterial())) { continue; } if(d.getMaterial().equals(Material.CACTUS)) { if(!block.getMaterial().equals(Material.SAND) && !block.getMaterial().equals(Material.RED_SAND)) { sliver.set(k, B.getBlockData("RED_SAND")); } } if(d.getMaterial().equals(Material.WHEAT) || d.getMaterial().equals(Material.CARROTS) || d.getMaterial().equals(Material.POTATOES) || d.getMaterial().equals(Material.MELON_STEM) || d.getMaterial().equals(Material.PUMPKIN_STEM)) { if(!block.getMaterial().equals(Material.FARMLAND)) { sliver.set(k, B.getBlockData("FARMLAND")); } } if(d.getBlockData() instanceof Bisected && k < 254) { FastBlockData bb = d.clone(); Bisected t = ((Bisected) d.getBlockData()); t.setHalf(Half.TOP); Bisected b = ((Bisected) bb.getBlockData()); b.setHalf(Half.BOTTOM); sliver.set(k + 1, bb); sliver.set(k + 2, d); } else { int stack = i.getHeight(rng.nextParallelRNG((int) (39456 + (10000 * i.getChance()) + i.getStackMax() + i.getStackMin() + i.getZoom())), rx, rz, getData()); FastBlockData top = null; if(stack > 1 && i.getTopPalette().hasElements()) { top = i.getBlockDataForTop(biome, rng.nextParallelRNG(38887 + biome.getRarity() + biome.getName().length() + j++), rx, rz, getData()); } if(stack == 1) { sliver.set(k + 1, d); } else if(k < 255 - stack) { for(int l = 0; l < stack; l++) { FastBlockData dd = i.getBlockData100(biome, rng.nextParallelRNG(38888 + l + biome.getRarity() + biome.getName().length() + j++), rx + l, rz - l, getData()); d = dd != null ? dd : d; sliver.set(k + l + 1, l == stack - 1 && top != null ? top : d); } } } break; } } } private void decorateCave(RNG rng, IrisBiome biome, AtomicSliver sliver, int k, int rx, int rz, FastBlockData block) { if(!getDimension().isDecorate()) { return; } int j = 0; for(IrisDecorator i : biome.getDecorators()) { FastBlockData d = i.getBlockData(biome, rng.nextParallelRNG(2333877 + biome.getRarity() + biome.getName().length() + +j++), rx, rz, getData()); if(d != null) { if(!B.canPlaceOnto(d.getMaterial(), block.getMaterial())) { continue; } if(d.getMaterial().equals(Material.CACTUS)) { if(!block.getMaterial().equals(Material.SAND) && !block.getMaterial().equals(Material.RED_SAND)) { sliver.set(k, B.getBlockData("SAND")); } } if(d.getBlockData() instanceof Bisected && k < 254) { FastBlockData bb = d.clone(); Bisected t = ((Bisected) d.getBlockData()); t.setHalf(Half.TOP); Bisected b = ((Bisected) bb.getBlockData()); b.setHalf(Half.BOTTOM); sliver.set(k + 1, bb); sliver.set(k + 2, d); } else { int stack = i.getHeight(rng.nextParallelRNG((int) (39456 + (1000 * i.getChance()) + i.getZoom() * 10)), rx, rz, getData()); if(stack == 1) { sliver.set(k + 1, d); } else if(k < 255 - stack) { for(int l = 0; l < stack; l++) { if(sliver.isSolid(k + l + 1)) { break; } sliver.set(k + l + 1, d); } } } break; } } } private void decorateUnderwater(RNG random, IrisBiome biome, AtomicSliver sliver, int y, int rx, int rz) { if(!getDimension().isDecorate()) { return; } int j = 0; for(IrisDecorator i : biome.getDecorators()) { if(biome.getInferredType().equals(InferredType.SHORE)) { continue; } FastBlockData d = i.getBlockData(biome, getMasterRandom().nextParallelRNG(2555 + biome.getRarity() + biome.getName().length() + j++), rx, rz, getData()); if(d != null) { int stack = i.getHeight(random.nextParallelRNG((int) (239456 + i.getStackMax() + i.getStackMin() + i.getVerticalZoom() + i.getZoom() + i.getBlockData(getData()).size() + j)), rx, rz, getData()); if(stack == 1) { sliver.set(i.getPartOf().equals(DecorationPart.SEA_SURFACE) ? (getFluidHeight() + 1) : (y + 1), d); } else if(y < getFluidHeight() - stack) { for(int l = 0; l < stack; l++) { sliver.set(i.getPartOf().equals(DecorationPart.SEA_SURFACE) ? (getFluidHeight() + 1 + l) : (y + l + 1), d); } } break; } } } @Override protected void onPostGenerate(RNG random, int x, int z, TerrainChunk terrain, HeightMap height, BiomeMap biomeMap, AtomicSliverMap map) { onPreParallaxPostGenerate(random, x, z, terrain, height, biomeMap, map); } protected void onPreParallaxPostGenerate(RNG random, int x, int z, TerrainChunk terrain, HeightMap height, BiomeMap biomeMap, AtomicSliverMap map) { if(getDimension().isRavines()) { getGlRavine().generateRavines(random.nextParallelRNG(x - 283845).nextParallelRNG(z + 23845868), x, z, terrain, height, biomeMap, map); } } public void generateDeposits(RNG rx, TerrainChunk terrain, int x, int z) { PrecisionStopwatch p = PrecisionStopwatch.start(); RNG ro = rx.nextParallelRNG(x * x).nextParallelRNG(z * z); IrisRegion region = sampleRegion((x * 16) + 7, (z * 16) + 7); IrisBiome biome = sampleTrueBiome((x * 16) + 7, (z * 16) + 7); for(IrisDepositGenerator k : getDimension().getDeposits()) { k.generate(terrain, ro, this, x, z, false); } for(IrisDepositGenerator k : region.getDeposits()) { for(int l = 0; l < ro.i(k.getMinPerChunk(), k.getMaxPerChunk()); l++) { k.generate(terrain, ro, this, x, z, false); } } for(IrisDepositGenerator k : biome.getDeposits()) { for(int l = 0; l < ro.i(k.getMinPerChunk(), k.getMaxPerChunk()); l++) { k.generate(terrain, ro, this, x, z, false); } } p.end(); getMetrics().getDeposits().put(p.getMilliseconds()); } protected void onPostParallaxPostGenerate(RNG random, int x, int z, TerrainChunk terrain, HeightMap height, BiomeMap biomeMap, AtomicSliverMap map) { } public double getNoiseHeight(int rx, int rz) { return getBiomeHeight(rx, rz); } public IrisBiome sampleTrueBiomeBase(int x, int z) { if(!getDimension().getFocus().equals("")) { return focus(); } int height = (int) Math.round(getTerrainHeight(x, z)); double wx = getModifiedX(x, z); double wz = getModifiedZ(x, z); IrisRegion region = sampleRegion(x, z); double sh = region.getShoreHeight(wx, wz); double shMax = getFluidHeight() + (sh / 2); double shMin = getFluidHeight() - (sh / 2); IrisBiome current = sampleBiome(x, z); InferredType aquaticType = current.isAquatic() ? (current.isSea() ? InferredType.SEA : current.isRiver() ? InferredType.RIVER : InferredType.LAKE) : InferredType.SEA; boolean sea = height <= getFluidHeight(); boolean shore = height >= shMin && height <= shMax; boolean land = height > getFluidHeight(); // Remove rivers, lakes & sea from land if(current.isAquatic() && land) { current = glBiome.generateData(InferredType.LAND, wx, wz, x, z, region); } // Remove land from underwater if(current.isLand() && sea) { current = glBiome.generateData(aquaticType, wx, wz, x, z, region); } // Add shores to land if(shore) { current = glBiome.generateData(InferredType.SHORE, wx, wz, x, z, region); } // Impure Remove rivers, lakes & sea from land if(current.isAquatic() && land) { current = glBiome.generatePureData(InferredType.LAND, wx, wz, x, z, region); } // Impure Remove land from underwater if(current.isLand() && sea) { current = glBiome.generatePureData(aquaticType, wx, wz, x, z, region); } // Impure Add shores to land if(shore) { current = glBiome.generatePureData(InferredType.SHORE, wx, wz, x, z, region); } return current; } public IrisBiome sampleCaveBiome(int x, int z) { double wx = getModifiedX(x, z); double wz = getModifiedZ(x, z); return glBiome.generateData(InferredType.CAVE, wx, wz, x, z, sampleRegion(x, z)); } public IrisBiome sampleTrueBiome(int x, int y, int z) { if(y < getTerrainHeight(x, z)) { double wx = getModifiedX(x, z); double wz = getModifiedZ(x, z); IrisBiome r = glBiome.generateData(InferredType.CAVE, wx, wz, x, z, sampleRegion(x, z)); if(r != null) { return r; } } return sampleTrueBiome(x, z); } public IrisRegion sampleRegion(int x, int z) { return getCache().getRegion(x, z); } public IrisBiome sampleTrueBiome(int x, int z) { if(!getDimension().getFocus().equals("")) { return focus(); } return getCache().getBiome(x, z); } @Override protected int onSampleColumnHeight(int cx, int cz, int rx, int rz, int x, int z) { return (int) Math.round(getTerrainHeight(rx, rz)); } private boolean touchesSea(int rx, int rz) { return isFluidAtHeight(rx + 1, rz) || isFluidAtHeight(rx - 1, rz) || isFluidAtHeight(rx, rz - 1) || isFluidAtHeight(rx, rz + 1); } public boolean isUnderwater(int x, int z) { return isFluidAtHeight(x, z); } public boolean isFluidAtHeight(int x, int z) { return Math.round(getTerrainHeight(x, z)) < getFluidHeight(); } public int getFluidHeight() { if(getDimension() == null) { return 63; } return getDimension().getFluidHeight(); } public double getTerrainHeight(int x, int z) { return getCache().getHeight(x, z); } public double getTerrainWaterHeight(int x, int z) { return Math.max(getTerrainHeight(x, z), getFluidHeight()); } @Override public void onHotload() { generatorSize = 0; super.onHotload(); getData().preferFolder(getDimension().getLoadFile().getParentFile().getParentFile().getName()); loadGenerators(); buildGenLayers(getMasterRandom()); } public void registerGenerator(IrisGenerator g) { KMap generators = this.generators; getRegionLock().lock(); if(g.getLoadKey() == null || generators.containsKey(g.getLoadKey())) { getRegionLock().unlock(); return; } generatorList.add(g); getRegionLock().unlock(); generators.put(g.getLoadKey(), g); } protected KMap getGenerators() { return generators; } protected double getRawBiomeHeight(double rrx, double rrz) { double h = 0; for(int j = 0; j < generatorSize; j++) { try { h += interpolateGenerator(rrx, rrz, generatorList.get(j)); } catch(Throwable e) { } } return h; } protected double getBiomeHeight(double rrx, double rrz) { double h = getRawBiomeHeight(rrx, rrz); for(IrisShapedGeneratorStyle i : getDimension().getOverlayNoise()) { h += i.get(getMasterRandom(), rrx, rrz); } return h; } protected double interpolateGenerator(double rx, double rz, IrisGenerator gen) { double hi = gen.getInterpolator().interpolate(rx, rz, (xx, zz) -> { try { return sampleBiome((int) xx, (int) zz).getGenLinkMax(gen.getLoadKey()); } catch(Throwable e) { Iris.warn("Failed to sample hi biome at " + rx + " " + rz + " using the generator " + gen.getLoadKey()); fail(e); } return 0; }); double lo = gen.getInterpolator().interpolate(rx, rz, (xx, zz) -> { try { return sampleBiome((int) xx, (int) zz).getGenLinkMin(gen.getLoadKey()); } catch(Throwable e) { Iris.warn("Failed to sample lo biome at " + rx + " " + rz + " using the generator " + gen.getLoadKey()); fail(e); } return 0; }); return M.lerp(lo, hi, gen.getHeight(rx, rz, getTarget().getSeed() + 239945)); } protected void loadGenerators() { generatorList.clear(); generators.clear(); loadGenerators(getDimension()); generatorSize = generatorList.size(); } protected void loadGenerators(IrisDimension dim) { if(dim == null) { Iris.warn("Cannot load generators, Dimension is null!"); return; } KList touch = new KList<>(); KList loadQueue = new KList<>(); for(String i : dim.getRegions()) { IrisRegion r = loadRegion(i); if(r != null) { loadQueue.addAll(r.getLandBiomes()); loadQueue.addAll(r.getSeaBiomes()); loadQueue.addAll(r.getShoreBiomes()); loadQueue.addAll(r.getRidgeBiomeKeys()); loadQueue.addAll(r.getSpotBiomeKeys()); } } while(!loadQueue.isEmpty()) { String next = loadQueue.pop(); if(!touch.contains(next)) { touch.add(next); IrisBiome biome = loadBiome(next); biome.getGenerators().forEach((i) -> registerGenerator(i.getCachedGenerator(this))); loadQueue.addAll(biome.getChildren()); } } Iris.info("Loaded " + generators.size() + " Generators"); } public IrisBiome computeRawBiome(int x, int z) { if(!getDimension().getFocus().equals("")) { IrisBiome biome = loadBiome(getDimension().getFocus()); for(String i : getDimension().getRegions()) { IrisRegion reg = loadRegion(i); if(reg.getLandBiomes().contains(biome.getLoadKey())) { biome.setInferredType(InferredType.LAND); break; } if(reg.getSeaBiomes().contains(biome.getLoadKey())) { biome.setInferredType(InferredType.SEA); break; } if(reg.getShoreBiomes().contains(biome.getLoadKey())) { biome.setInferredType(InferredType.SHORE); break; } } return biome; } double wx = getModifiedX(x, z); double wz = getModifiedZ(x, z); IrisRegion region = glBiome.getRegion(wx, wz); return glBiome.generateRegionData(wx, wz, x, z, region); } public IrisBiome sampleBiome(int x, int z) { return getCache().getRawBiome(x, z); } }