From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: JellySquid Date: Tue, 4 Aug 2020 21:06:02 +0200 Subject: [PATCH] lithium-MixinChunkGeneratorAbstract Original code by JellySquid, licensed under GNU Lesser General Public License v3.0 you can find the original code on https://github.com/jellysquid3/lithium-fabric/tree/1.16.x/fabric (Yarn mappings) Co-authored-by: tr7zw diff --git a/src/main/java/me/jellysquid/mods/lithium/common/world/noise/SimplexNoiseCache.java b/src/main/java/me/jellysquid/mods/lithium/common/world/noise/SimplexNoiseCache.java new file mode 100644 index 0000000000000000000000000000000000000000..e6cd19e75a88f4dbf38d3cc63883068d95aa67d4 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/lithium/common/world/noise/SimplexNoiseCache.java @@ -0,0 +1,135 @@ +package me.jellysquid.mods.lithium.common.world.noise; + +import it.unimi.dsi.fastutil.HashCommon; + +import net.minecraft.server.ChunkCoordIntPair; +import net.minecraft.server.MathHelper; +import net.minecraft.server.NoiseGenerator3Handler; + +import java.util.Arrays; + +/** + * A cache for the End's noise generator that caches computed values. Through the caching, we can eliminate a large + * amount of overhead in computing the noise values several hundred thousand times per chunk. This code uses the same + * array backed lossy cache as FastCachingLayerSampler. + */ +public class SimplexNoiseCache { + private static final int GRID_SIZE = 2; + private static final float MIN = -100.0F; + private static final float MAX = 80.0F; + private static final float ISLAND_RADIUS = 100.0F; + // The smallest encompassing power of two that can store all of the noise values in a chunk. + private static final int CACHE_SIZE = 8192; + + private final int mask; + private final long[] keys; + private final float[] values; + + private final NoiseGenerator3Handler sampler; + + public SimplexNoiseCache(NoiseGenerator3Handler sampler) { + this.sampler = sampler; + + this.mask = CACHE_SIZE - 1; + + // Initialize default values + this.keys = new long[CACHE_SIZE]; + Arrays.fill(this.keys, Long.MIN_VALUE); + this.values = new float[CACHE_SIZE]; + } + + /** + * Attempt to get the cached distance factor, saving computation time. + */ + private float getDistanceFactor(int x, int z) { + // Hash key and get index + long key = ChunkCoordIntPair.pair(x, z); + int idx = (int) HashCommon.mix(key) & this.mask; + + if (this.keys[idx] == key) { + // Cache hit, return cached value + return this.values[idx]; + } + + // Cache miss, compute and store value + + // A marker for no value. + float value = -1.0F; + + long lx = x; + long lz = z; + long distanceFromOriginSq = lx * lx + lz * lz; + + // Ensure we are 64 grid cells away from the origin. + if (distanceFromOriginSq > 64 * 64) { + // Reduce the number of island-forming grid cells by sampling noise with a threshold + if (this.sampler.a(x, z) < -0.9) { + // Generate a pseudo-random value from 9 to 21 + value = (Math.abs(x) * 3439.0F + Math.abs(z) * 147.0F) %13.0F + 9.0F; + } + } + + // Store values in cache + this.keys[idx] = key; + this.values[idx] = value; + + return value; + } + + /** + * Mapped and cleaned up implementation of the End biome source's sampler. Tries to use cached values wherever + * possible. + */ + public float getNoiseAt(int x, int z) { + // [VanillaCopy] TheEndBiomeSource#getNoiseAt + + int gridX = x / GRID_SIZE; + int gridZ = z / GRID_SIZE; + + // This is the "center point", offset to center around the current grid cell + int gridOriginX = x % GRID_SIZE; + int gridOriginZ = z % GRID_SIZE; + + // Initialize density for the central island + float density = ISLAND_RADIUS - MathHelper.sqrt(x * x + z * z) * 8.0F; + if (density >= MAX) { + return MAX; + } + + // Iterate through 25x25 grid cells + for (int offsetX = -12; offsetX <= 12; offsetX++) { + for (int offsetZ = -12; offsetZ <= 12; offsetZ++) { + int globalGridX = gridX + offsetX; + int globalGridZ = gridZ + offsetZ; + + // Try to retrieve values from cache + float distanceFactor = getDistanceFactor(globalGridX, globalGridZ); + if (distanceFactor != -1.0F) { + // Compute the distance to the origin + float deltaX = gridOriginX - offsetX * GRID_SIZE; + float deltaZ = gridOriginZ - offsetZ * GRID_SIZE; + + // Calculate the density at this grid cell + float scaledDistance = MathHelper.sqrt(deltaX * deltaX + deltaZ * deltaZ) * distanceFactor; + float densityHere = ISLAND_RADIUS - scaledDistance; + + // Try to return early if we're over the max + if (densityHere > density) { + if (densityHere >= MAX) { + return MAX; + } + + density = densityHere; + } + } + } + } + + // Avoid a call to Math.max because the density cannot be bigger than the max. + if (density < MIN) { + return MIN; + } + + return density; + } +} diff --git a/src/main/java/net/minecraft/server/ChunkGeneratorAbstract.java b/src/main/java/net/minecraft/server/ChunkGeneratorAbstract.java index 321ebe8891defb46f3ad3dfea37c9bb6e2025ad7..119804e5c12aca3f4fbfeaf424f46152f43b3941 100644 --- a/src/main/java/net/minecraft/server/ChunkGeneratorAbstract.java +++ b/src/main/java/net/minecraft/server/ChunkGeneratorAbstract.java @@ -57,12 +57,13 @@ public final class ChunkGeneratorAbstract extends ChunkGenerator { private final NoiseGenerator t; private final NoiseGeneratorOctaves u; @Nullable - private final NoiseGenerator3Handler v; + private final NoiseGenerator3Handler v; // Yatopia - yarn "SimplexNoiseSampler" protected final IBlockData f; protected final IBlockData g; private final long w; protected final Supplier h; private final int x; + private ThreadLocal tlCache; // Yatopia public ChunkGeneratorAbstract(WorldChunkManager worldchunkmanager, long i, Supplier supplier) { this(worldchunkmanager, worldchunkmanager, i, supplier); @@ -99,7 +100,7 @@ public final class ChunkGeneratorAbstract extends ChunkGenerator { } else { this.v = null; } - + this.tlCache = ThreadLocal.withInitial(() -> new me.jellysquid.mods.lithium.common.world.noise.SimplexNoiseCache(v)); // Yatopia } @Override @@ -164,7 +165,8 @@ public final class ChunkGeneratorAbstract extends ChunkGenerator { double d3; if (this.v != null) { - d0 = (double) (WorldChunkManagerTheEnd.a(this.v, i, j) - 8.0F); + //d0 = (double) (WorldChunkManagerTheEnd.a(this.v, i, j) - 8.0F); // Yatopia + d0 = tlCache.get().getNoiseAt(i, j) - 8.0F; // Yatopia if (d0 > 0.0D) { d1 = 0.25D; } else {