12 | * *
13 | * * This software is placed in the public domain and is provided as is
14 | * * without express or implied warranty.
15 | * *
16 | * * Basic implementation of Steve Worley's noise function; see proceedings
17 | * * of SIGGRAPH 96.
18 | * */
19 | */
20 |
21 | import supercoder79.simplexterrain.api.noise.Noise;
22 |
23 | /**
24 | * This is an implementation of Steve Worley's cellular noise function. It is
25 | * derived (heavily) from Matt Pharr's public domain implementation, and in the
26 | * spirit of that donation this version remains in the public domain.
27 | *
28 | * Here is the original licensing information:
29 | *
30 | *
31 | * /*
32 | * * wnoise.cpp
33 | * *
34 | * * Copyright (C) 1998, Matt Pharr <mmp@graphics.stanford.edu>
35 | * *
36 | * * This software is placed in the public domain and is provided as is without
37 | * * express or implied warranty.
38 | * *
39 | * * Basic implementation of Steve Worley's noise function; see proceedings of
40 | * * SIGGRAPH 96.
41 | * */
42 | *
43 | *
44 | * Some features (like variable density) have been removed for simplicity.
45 | *
46 | * Instances of this class are immutable and threadsafe.
47 | *
48 | * @author saybur
49 | *
50 | */
51 | public class WorleyNoise extends Noise {
52 | /**
53 | * Represents a point in three dimensional space.
54 | *
55 | * @author saybur
56 | *
57 | */
58 | private static final class Point {
59 | private final double x;
60 | private final double z;
61 |
62 | /**
63 | * Creates a point at location (x, z).
64 | *
65 | * @param x
66 | * the x coordinate of the point.
67 | * @param z
68 | * the z coordinate of the point.
69 | */
70 | private Point(double x, double z) {
71 | this.x = x;
72 | this.z = z;
73 | }
74 |
75 | /**
76 | * Provides a fast distance calculation between two points. This is done by
77 | * not taking the square root of the result.
78 | *
79 | * @param other
80 | * the coordinate to calculate distance to.
81 | * @return the distance between this point and the provided point.
82 | */
83 | public double distanceSquared(Point other) {
84 | double x2 = x - other.x;
85 | double z2 = z - other.z;
86 | return x2 * x2 + z2 * z2;
87 | }
88 | }
89 |
90 | private static int floor(double n) {
91 | return n > 0 ? (int) n : (int) n - 1;
92 | }
93 |
94 | private static double frac(double n) {
95 | return n >= 0 ? n - (int) (n) : frac(-n);
96 | }
97 |
98 | /**
99 | * Checks all voxels near the origin for the closest point to the origin.
100 | * The returned value will be the distance to the closest point.
101 | */
102 | private static double minimumDistance(XorShift.Instance r, Point origin) {
103 | // hack, but easier than handling points that are exactly at negative
104 | // integer latice-points correctly.
105 | Point p = new Point(origin.x + 1e-7, origin.z + 1e-7);
106 | // get the coordinate that this point resides at
107 | int x = floor(p.x);
108 | // int y = floor(p.y);
109 | int z = floor(p.z);
110 | // create storage to track lowest values
111 | double s = Double.MAX_VALUE;
112 | // first check voxel the point is in
113 | s = processVoxel(r, p, s, x, z);
114 | // check each of the voxels that share a face with the
115 | // point's voxel, if they're close enough to possibly
116 | // make a difference
117 | // squared distance to the voxel in the +x direction
118 | double dpx2 = p.x >= 0. ? square(1.0 - frac(p.x)) : square(frac(p.x));
119 | if(dpx2 < s) {
120 | s = processVoxel(r, p, s, x + 1, z);
121 | }
122 | // -x
123 | double dnx2 = p.x >= 0. ? square(frac(p.x)) : square(1. - frac(p.x));
124 | if(dnx2 < s) {
125 | s = processVoxel(r, p, s, x - 1, z);
126 | }
127 |
128 | //We can safely ignore the y value, improving the performance
129 |
130 | // // +y
131 | // double dpy2 = p.y >= 0. ? square(1. - frac(p.y)) : square(frac(p.y));
132 | // if(dpy2 < s) {
133 | // s = processVoxel(r, p, s, x, y + 1, z);
134 | // }
135 | // // -y
136 | // double dny2 = p.y >= 0. ? square(frac(p.y)) : square(1. - frac(p.y));
137 | // if(dny2 < s) {
138 | // s = processVoxel(r, p, s, x, y - 1, z);
139 | // }
140 |
141 | // +z
142 | double dpz2 = p.z >= 0. ? square(1. - frac(p.z)) : square(frac(p.z));
143 | if(dpz2 < s) {
144 | s = processVoxel(r, p, s, x, z + 1);
145 | }
146 | // -z
147 | double dnz2 = p.z >= 0. ? square(frac(p.z)) : square(1. - frac(p.z));
148 | if(dnz2 < s) {
149 | s = processVoxel(r, p, s, x, z - 1);
150 | }
151 | // finally check the remaining adjacent voxels
152 | for(int i = -1; i <= 1; ++i) {
153 | // for(int j = -1; j <= 1; ++j) {
154 | for(int k = -1; k <= 1; ++k) {
155 | // don't check the ones we already did above
156 | if(Noise.fastAbs(i) + /*Math.abs(j)*/ + Noise.fastAbs(k) <= 1) {
157 | continue;
158 | }
159 | // find squared distance to voxel
160 | double vd2 = 0;
161 | if(i < 0)
162 | vd2 += dnx2;
163 | else if(i > 0)
164 | vd2 += dpx2;
165 | // if(j < 0)
166 | // vd2 += dny2;
167 | // else if(j > 0)
168 | // vd2 += dpy2;
169 | if(k < 0)
170 | vd2 += dnz2;
171 | else if(k > 0)
172 | vd2 += dpz2;
173 | // and check it if it's close enough to matter
174 | if(vd2 < s)
175 | {
176 | s = processVoxel(r, p, s, x + i, z + k);
177 | }
178 | }
179 | // }
180 | }
181 | // provide minimum. be sure to square root it to get the
182 | // true distance.
183 |
184 | return Math.sqrt(s);
185 | }
186 |
187 | /**
188 | * Processes a voxel and calculates the distances of the points within
189 | * against the provided point. It also tracks the progress of the lowest
190 | * values yet discovered.
191 | *
192 | * @param r
193 | * the random number generator.
194 | * @param p
195 | * the point that the locations within this voxel will be tested
196 | * against.
197 | * @param s
198 | * the storage that tracks the lowest values currently
199 | * encountered.
200 | * @param x
201 | * the x coordinate of the voxel.
202 | * @param z
203 | * the z coordinate of the voxel.
204 | * @return the closest distance of the points within the voxel to the
205 | * provided point.
206 | */
207 | private static double processVoxel(XorShift.Instance r, Point p, double s, int x, int z) {
208 | // reset random number generator for the voxel
209 | r.setSeed(x, z);
210 | // each voxel always has one point
211 | // Point created = new Point(
212 | // x + r.nextDouble(),
213 | // y + r.nextDouble(),
214 | // z + r.nextDouble());
215 | Point created = new Point(
216 | x + r.nextDouble(),
217 | z + r.nextDouble());
218 | // determine the distance between the generated point
219 | // and the source point we're checking.
220 | double distance = p.distanceSquared(created);
221 | // add distance if it is lowest
222 | return Math.min(distance, s);
223 | }
224 |
225 | private static double square(double n) {
226 | return n * n;
227 | }
228 |
229 | private final XorShift randomFactory;
230 |
231 | public WorleyNoise(long seed) {
232 | super(seed);
233 | randomFactory = XorShift.create(seed);
234 | }
235 |
236 | /**
237 | * Gets the noise value at the provided location.
238 | *
239 | * @param x
240 | * the x coordinate.
241 | * @param y
242 | * the y coordinate.
243 | * @param z
244 | * the z coordinate.
245 | * @return the noise value at the coordinate.
246 | */
247 | private double noise(double x, double y, double z) {
248 | return minimumDistance(randomFactory.getInstance(), new Point(x, z));
249 | }
250 |
251 | @Override
252 | public double sample(double x, double z) {
253 | return noise(x, 0, z);
254 | }
255 |
256 | @Override
257 | public double sample(double x, double y, double z) {
258 | return noise(x, y, z);
259 | }
260 | }
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/noise/worley/XorShift.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.noise.worley;
2 |
3 | import java.util.Objects;
4 | import java.util.Random;
5 | import java.util.concurrent.ThreadLocalRandom;
6 |
7 | import supercoder79.simplexterrain.api.noise.Noise;
8 |
9 | /**
10 | * Random number generator for voxel-based fractals based on the XorShift
11 | * pseudo-random number generator described by George Marsaglia in his paper
12 | * XorShift RNGs (http://www.jstatsoft.org/v08/i14/paper).
14 | *
15 | * This particular implementation has a period of 2 ^ 96 - 1
and
16 | * uses Marsaglia's triple of [13, 19, 3]. For use in cellular noise functions,
17 | * it has a method for resetting the generator state via
18 | * {@link Instance#setSeed(long, long, long)} for x, y, z
voxel
19 | * coordinates, which will wrap on [0, 1024) for each of x, y, z
.
20 | *
21 | * Objects of XorShift.Instance
are not threadsafe.
22 | * Concurrent access should be externally synchronized or use separate
23 | * instances. XorShift
itself is threadsafe.
24 | *
25 | * @author saybur
26 | *
27 | */
28 | public class XorShift {
29 | /**
30 | * Random number generator instance for XorShift
. See the class
31 | * documentation for details.
32 | *
33 | * A reminder: this is not threadsafe.
34 | *
35 | * @author saybur
36 | *
37 | */
38 | public final class Instance {
39 | private long x, z;
40 |
41 | private Instance() {
42 | setSeed(0, 0);
43 | }
44 |
45 | /**
46 | * Provides a pseudo-random double
value on [0.0, 1.0).
47 | *
48 | * @return the next double value.
49 | */
50 | public double nextDouble() {
51 | // get out of signed range, then divide in the remaining space
52 | // of the shifted long value. i hope this works...
53 | return (nextLong() >>> 2) * (1.0 / (1L << 62));
54 | }
55 |
56 | /**
57 | * Provides a pseudo-random long
value.
58 | *
59 | * @return the next long
value.
60 | */
61 | public long nextLong() {
62 | long t;
63 | t = (x ^ (x << 13));
64 | x = z;
65 | z = (z ^ (z >>> 3)) ^ (t ^ t >>> 19);
66 | return z;
67 | }
68 |
69 | /**
70 | * Sets the seed for this number generator to the given values.
71 | *
72 | * This is for the cellular noise functions and may be ignored by other
73 | * users, as the constructor will initialize the generator with random
74 | * starting values.
75 | *
76 | * @param x
77 | * the X component.
78 | * @param z
79 | * the Z component.
80 | */
81 | public void setSeed(long x, long z) {
82 | this.x = seeds[(int) (Noise.fastAbs(x) % SEED_TABLE_SPACE)];
83 | // this.y = seeds[(int) (Noise.fastAbs(y) % SEED_TABLE_SPACE)
84 | // + SEED_TABLE_SPACE];
85 | this.z = seeds[(int) (Noise.fastAbs(z) % SEED_TABLE_SPACE)
86 | + SEED_TABLE_SPACE + SEED_TABLE_SPACE];
87 |
88 | // if (!SimplexTerrain.CONFIG.sacrificeAccuracyForSpeed) {
89 | // // decreases spherical artifacts, but obviously is slow
90 | // // TODO replace by fixing underlying issue
91 | // for (int i = 0; i < 5; i++)
92 | // nextLong();
93 | // }
94 | }
95 | }
96 |
97 | /**
98 | * The space for each of x, y, and z in the seeds table.
99 | */
100 | private static final int SEED_TABLE_SPACE = 1024;
101 | /**
102 | * The final size of the seeds table.
103 | */
104 | private static final int SEED_TABLE_SIZE = SEED_TABLE_SPACE * 3;
105 |
106 | /**
107 | * Creates a new random number generator factory with a random seed value.
108 | *
109 | * @return the random number generator factory.
110 | */
111 | public static XorShift create() {
112 | return new XorShift(ThreadLocalRandom.current().nextLong());
113 | }
114 |
115 | /**
116 | * Creates a new random number generator factory with the given seed value.
117 | *
118 | * @param seed
119 | * the seed value.
120 | * @return the random number generator factory.
121 | */
122 | public static XorShift create(long seed) {
123 | return new XorShift(seed);
124 | }
125 |
126 | private final long[] seeds;
127 |
128 | private XorShift(long seed) {
129 | Random random = new Random(seed);
130 | seeds = new long[SEED_TABLE_SIZE];
131 | for(int i = 0; i < SEED_TABLE_SIZE; i++) {
132 | seeds[i] = random.nextLong();
133 | }
134 | }
135 |
136 | @Override
137 | public boolean equals(Object obj) {
138 | if(obj instanceof XorShift) {
139 | XorShift o = (XorShift) obj;
140 | return Objects.equals(seeds, o.seeds);
141 | }
142 | else {
143 | return false;
144 | }
145 | }
146 |
147 | /**
148 | * Creates a new random number generator instance.
149 | *
150 | * Each instance should be used by a single thread only. See the class
151 | * documentation for information about the generator itself.
152 | *
153 | * @return a new random number generator instance.
154 | */
155 | public XorShift.Instance getInstance() {
156 | return new XorShift.Instance();
157 | }
158 |
159 | @Override
160 | public int hashCode() {
161 | return Objects.hash(seeds);
162 | }
163 | }
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/BiomeData.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world;
2 |
3 | /**
4 | * Extra data for biome generation at a specific x/z coordinate.
5 | * This can be used to set specific biomes, force generation, and do other tasks.
6 | */
7 | public class BiomeData {
8 | private int height = 0;
9 | private boolean river = false;
10 | private boolean mushroomIsland = false;
11 | private boolean forcedLowlands = false;
12 |
13 | public void setHeight(int height) {
14 | this.height = height;
15 | }
16 |
17 | public int getHeight() {
18 | return height;
19 | }
20 |
21 | public void setRiver() {
22 | this.river = true;
23 | }
24 |
25 | public boolean isRiver() {
26 | return river;
27 | }
28 |
29 | public boolean isMushroomIsland() {
30 | return mushroomIsland;
31 | }
32 |
33 | public void setMushroomIsland() {
34 | this.mushroomIsland = true;
35 | }
36 |
37 | public boolean isForcedLowlands() {
38 | return forcedLowlands;
39 | }
40 |
41 | public void setForcedLowlands() {
42 | this.forcedLowlands = true;
43 | }
44 |
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/README.md:
--------------------------------------------------------------------------------
1 | ## SuperCoder's random reference for worldgen stuff
2 |
3 |
4 | ### Subchunk Position
5 |
6 | `v` = x or z position
7 | Subchunk: `v >> 2`
8 | to blockpos: `v << 2`
9 | from blockpos to position in chunk: `v & 15`
10 |
11 | ### Caching
12 | * Cache noise modifiers (called multiple times per blockpos)
13 | * Do not cache post processors (called once per blockpos)
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/SimplexWorldType.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world;
2 |
3 | import net.minecraft.client.world.GeneratorType;
4 | import net.minecraft.util.registry.Registry;
5 | import net.minecraft.world.biome.Biome;
6 | import net.minecraft.world.gen.chunk.ChunkGenerator;
7 | import net.minecraft.world.gen.chunk.ChunkGeneratorSettings;
8 | import supercoder79.simplexterrain.world.gen.SimplexBiomeSource;
9 | import supercoder79.simplexterrain.world.gen.SimplexChunkGenerator;
10 |
11 | public class SimplexWorldType extends GeneratorType {
12 | public SimplexWorldType() {
13 | super("simplex");
14 | GeneratorType.VALUES.add(this);
15 | }
16 |
17 | @Override
18 | protected ChunkGenerator getChunkGenerator(Registry biomeRegistry, Registry chunkGeneratorSettingsRegistry, long seed) {
19 | return new SimplexChunkGenerator(new SimplexBiomeSource(biomeRegistry, seed), seed);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/biome/BiomePicker.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.biome;
2 |
3 | import java.util.List;
4 |
5 | import com.google.common.collect.Lists;
6 |
7 | import net.minecraft.util.Identifier;
8 | import net.minecraft.util.registry.Registry;
9 | import net.minecraft.world.biome.Biome;
10 | import net.minecraft.world.biome.layer.util.LayerRandomnessSource;
11 |
12 | /**
13 | * Picks a biome. Pretty self explanatory.
14 | *
15 | * @author Valoeghese
16 | */
17 | public class BiomePicker {
18 | private final List biomeEntries = Lists.newArrayList();
19 | private double weightTotal;
20 |
21 | public Identifier pickBiome(LayerRandomnessSource rand) {
22 | double randVal = target(rand);
23 | int i = -1;
24 |
25 | while (randVal >= 0) {
26 | ++i;
27 | randVal -= biomeEntries.get(i).weight;
28 | }
29 |
30 | return biomeEntries.get(i).biomeId;
31 | }
32 |
33 | public void addBiome(Identifier biome, double weight) {
34 | this.biomeEntries.add(new Entry(biome, weight));
35 | weightTotal += weight;
36 | }
37 |
38 | private double target(LayerRandomnessSource random) {
39 | return (double) random.nextInt(Integer.MAX_VALUE) * weightTotal / Integer.MAX_VALUE;
40 | }
41 |
42 | public static class Entry {
43 | private final Identifier biomeId;
44 | private final double weight;
45 | public Entry(Identifier biome, double weight) {
46 | this.biomeId = biome;
47 | this.weight = weight;
48 | }
49 |
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/biome/SimplexBiomesImpl.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.biome;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | import net.minecraft.util.Identifier;
7 | import net.minecraft.util.Pair;
8 | import net.minecraft.util.registry.Registry;
9 | import net.minecraft.world.biome.Biome;
10 | import supercoder79.simplexterrain.api.biomes.SimplexClimate;
11 |
12 | public final class SimplexBiomesImpl {
13 | private SimplexBiomesImpl() {
14 | }
15 |
16 | private static final Map lowlandBiomes = new HashMap<>();
17 | private static final Map midlandBiomes = new HashMap<>();
18 | private static final Map highlandBiomes = new HashMap<>();
19 | private static final Map mountainPeaksBiomes = new HashMap<>();
20 |
21 | private static final Map> replacementBiomes = new HashMap<>();
22 |
23 | public static void addToLowlands(Identifier biome, SimplexClimate climate, double weight) {
24 | lowlandBiomes.computeIfAbsent(climate, simplexClimate -> new BiomePicker()).addBiome(biome, weight);
25 | }
26 | public static void addToMidlands(Identifier biome, SimplexClimate climate, double weight) {
27 | midlandBiomes.computeIfAbsent(climate, simplexClimate -> new BiomePicker()).addBiome(biome, weight);
28 | }
29 | public static void addToHighlands(Identifier biome, SimplexClimate climate, double weight) {
30 | highlandBiomes.computeIfAbsent(climate, simplexClimate -> new BiomePicker()).addBiome(biome, weight);
31 | }
32 | public static void addToMountainPeaks(Identifier biome, SimplexClimate climate, double weight) {
33 | mountainPeaksBiomes.computeIfAbsent(climate, simplexClimate -> new BiomePicker()).addBiome(biome, weight);
34 | }
35 |
36 | public static void addReplacementBiome(Identifier biomeToReplace, Identifier replacement, int chance) {
37 | // replacementBiomes.put(Registry.BIOME.get(biomeToReplace), new Pair<>(Registry.BIOME.get(replacement), chance));
38 | }
39 |
40 | public static BiomePicker getLowlandsBiomePicker(SimplexClimate climate) {
41 | return lowlandBiomes.get(climate);
42 | }
43 | public static BiomePicker getMidlandsBiomePicker(SimplexClimate climate) {
44 | return midlandBiomes.get(climate);
45 | }
46 | public static BiomePicker getHighlandsBiomePicker(SimplexClimate climate) {
47 | return highlandBiomes.get(climate);
48 | }
49 | public static BiomePicker getMountainPeaksBiomePicker(SimplexClimate climate) {
50 | return mountainPeaksBiomes.get(climate);
51 | }
52 |
53 | public static Map> getReplacementBiomes() {
54 | return replacementBiomes;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/biomelayers/SimplexBiomeLayers.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.biomelayers;
2 |
3 | import java.util.function.LongFunction;
4 |
5 | import net.minecraft.util.registry.Registry;
6 | import net.minecraft.world.biome.Biome;
7 | import net.minecraft.world.biome.layer.ScaleLayer;
8 | import net.minecraft.world.biome.layer.type.ParentedLayer;
9 | import net.minecraft.world.biome.layer.util.CachingLayerContext;
10 | import net.minecraft.world.biome.layer.util.LayerFactory;
11 | import net.minecraft.world.biome.layer.util.LayerSampleContext;
12 | import net.minecraft.world.biome.layer.util.LayerSampler;
13 | import net.minecraft.world.biome.source.BiomeLayerSampler;
14 | import supercoder79.simplexterrain.world.biomelayers.layers.*;
15 |
16 | public class SimplexBiomeLayers {
17 | private static > LayerFactory stack(long l, ParentedLayer parentedLayer, LayerFactory layerFactory, int i, LongFunction longFunction) {
18 | LayerFactory result = layerFactory;
19 |
20 | for(int j = 0; j < i; ++j) {
21 | result = parentedLayer.create(longFunction.apply(l + (long)j), result);
22 | }
23 |
24 | return result;
25 | }
26 |
27 | private static > LayerFactory build(Registry biomes, long worldSeed, LongFunction contextProvider) {
28 | LayerFactory layer = new SimplexClimateLayer(worldSeed).create(contextProvider.apply(1L));
29 | layer = new BaseBiomesLayer(biomes).create(contextProvider.apply(5L), layer);
30 | layer = stack(1000, ScaleLayer.NORMAL, layer, 7, contextProvider);
31 |
32 | return layer;
33 | }
34 |
35 | public static BiomeLayerSampler build(Registry biomes, long seed) {
36 | return new BiomeLayerSampler(build(biomes, seed, salt -> new CachingLayerContext(5, seed, salt)));
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/biomelayers/layers/BaseBiomesLayer.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.biomelayers.layers;
2 |
3 | import net.minecraft.util.registry.Registry;
4 | import net.minecraft.world.biome.Biome;
5 | import net.minecraft.world.biome.BiomeKeys;
6 | import net.minecraft.world.biome.layer.type.IdentitySamplingLayer;
7 | import net.minecraft.world.biome.layer.util.LayerRandomnessSource;
8 |
9 | public class BaseBiomesLayer implements IdentitySamplingLayer {
10 | private final Registry registry;
11 |
12 | public BaseBiomesLayer(Registry registry) {
13 |
14 | this.registry = registry;
15 | }
16 |
17 | @Override
18 | public int sample(LayerRandomnessSource context, int value) {
19 | if (context.nextInt(4) == 0) {
20 | return this.registry.getRawId(this.registry.get(BiomeKeys.DESERT));
21 | }
22 |
23 | return this.registry.getRawId(context.nextInt(3) == 0 ? this.registry.get(BiomeKeys.FOREST) : this.registry.get(BiomeKeys.PLAINS));
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/biomelayers/layers/ClimateTransformerLayer.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.biomelayers.layers;
2 |
3 | import java.util.function.ToIntFunction;
4 |
5 | import net.minecraft.util.registry.Registry;
6 | import net.minecraft.world.biome.Biome;
7 | import net.minecraft.world.biome.layer.type.IdentitySamplingLayer;
8 | import net.minecraft.world.biome.layer.util.LayerRandomnessSource;
9 | import supercoder79.simplexterrain.api.biomes.SimplexClimate;
10 |
11 | public class ClimateTransformerLayer implements IdentitySamplingLayer {
12 |
13 | public static ClimateTransformerLayer shore(Registry biomes) {
14 | return new ClimateTransformerLayer(climate -> biomes.getRawId(biomes.get(climate.oceanSet.shore)));
15 | }
16 |
17 | public static ClimateTransformerLayer ocean(Registry biomes) {
18 | return new ClimateTransformerLayer(climate -> biomes.getRawId(biomes.get(climate.oceanSet.ocean)));
19 | }
20 |
21 | public static ClimateTransformerLayer deepOcean(Registry biomes) {
22 | return new ClimateTransformerLayer(climate -> biomes.getRawId(biomes.get(climate.oceanSet.deepOcean)));
23 | }
24 |
25 | private final ToIntFunction transformer;
26 |
27 | ClimateTransformerLayer(ToIntFunction transformer) {
28 | this.transformer = transformer;
29 | }
30 |
31 | @Override
32 | public int sample(LayerRandomnessSource rand, int value) {
33 | return this.transformer.applyAsInt(SimplexClimateLayer.REVERSE_ID_MAP[value]);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/biomelayers/layers/SimplexClimateLayer.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.biomelayers.layers;
2 |
3 | import java.util.Random;
4 |
5 | import net.minecraft.world.biome.layer.type.InitLayer;
6 | import net.minecraft.world.biome.layer.util.LayerRandomnessSource;
7 | import supercoder79.simplexterrain.SimplexTerrain;
8 | import supercoder79.simplexterrain.api.biomes.SimplexClimate;
9 | import supercoder79.simplexterrain.api.noise.OctaveNoiseSampler;
10 | import supercoder79.simplexterrain.noise.gradient.OpenSimplexNoise;
11 |
12 | public class SimplexClimateLayer implements InitLayer {
13 | private OctaveNoiseSampler> temperatureNoise;
14 | private OctaveNoiseSampler> humidityNoise;
15 |
16 | private final long worldSeed;
17 |
18 | private int tempOffsetX;
19 | private int tempOffsetZ;
20 | private int humidityOffsetX;
21 | private int humidityOffsetZ;
22 |
23 | private static final Random RAND = new Random();
24 |
25 | public SimplexClimateLayer(long worldSeed) {
26 | this.worldSeed = worldSeed;
27 | this.initialiseNoise();
28 | }
29 |
30 | public void initialiseNoise() {
31 | RAND.setSeed(worldSeed);
32 | tempOffsetX = RAND.nextInt(10000);
33 | tempOffsetZ = RAND.nextInt(10000);
34 | humidityOffsetX = RAND.nextInt(10000);
35 | humidityOffsetZ = RAND.nextInt(10000);
36 | temperatureNoise = new OctaveNoiseSampler<>(OpenSimplexNoise.class, RAND, SimplexTerrain.CONFIG.temperatureOctaveAmount, SimplexTerrain.CONFIG.temperatureFrequency, SimplexTerrain.CONFIG.temperatureAmplitude, SimplexTerrain.CONFIG.temperatureAmplitude);
37 | humidityNoise = new OctaveNoiseSampler<>(OpenSimplexNoise.class, RAND, SimplexTerrain.CONFIG.humidityOctaveAmount, SimplexTerrain.CONFIG.humidityFrequency, SimplexTerrain.CONFIG.humidityAmplitude, SimplexTerrain.CONFIG.humidityAmplitude);
38 | }
39 |
40 | @Override
41 | public int sample(LayerRandomnessSource rand, int x, int z) {
42 | double temperature = temperatureNoise.sample(tempOffsetX + x, tempOffsetZ + z) + SimplexTerrain.CONFIG.temperatureOffset;
43 | double humidity = humidityNoise.sample(humidityOffsetX + x, humidityOffsetZ + z) + SimplexTerrain.CONFIG.humidityOffset;
44 |
45 | return SimplexClimate.fromTemperatureHumidity(temperature, humidity).id;
46 | }
47 |
48 | public static final SimplexClimate[] REVERSE_ID_MAP = new SimplexClimate[10];
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/blend/CachingBlender.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.blend;
2 |
3 | import it.unimi.dsi.fastutil.HashCommon;
4 | import net.minecraft.util.math.ChunkPos;
5 |
6 | import java.util.Arrays;
7 |
8 | public final class CachingBlender {
9 | private final long[] keys;
10 | private final LinkedBiomeWeightMap[] values;
11 | private final ScatteredBiomeBlender internal;
12 |
13 | public CachingBlender(double samplingFrequency, double blendRadiusPadding, int chunkWidth) {
14 | this.internal = new ScatteredBiomeBlender(samplingFrequency, blendRadiusPadding, chunkWidth);
15 |
16 | this.keys = new long[512];
17 | this.values = new LinkedBiomeWeightMap[512];
18 |
19 | Arrays.fill(this.keys, Long.MIN_VALUE);
20 | }
21 |
22 | public LinkedBiomeWeightMap getBlendForChunk(long seed, int chunkBaseWorldX, int chunkBaseWorldZ, ScatteredBiomeBlender.BiomeEvaluationCallback callback) {
23 | long key = key(chunkBaseWorldX, chunkBaseWorldZ);
24 | int idx = hash(key) & 511;
25 |
26 | if (this.keys[idx] == key) {
27 | return this.values[idx];
28 | }
29 |
30 | LinkedBiomeWeightMap weightMap = this.internal.getBlendForChunk(seed, chunkBaseWorldX, chunkBaseWorldZ, callback);
31 | this.values[idx] = weightMap;
32 | this.keys[idx] = key;
33 |
34 | return weightMap;
35 | }
36 |
37 | private static int hash(long key) {
38 | return (int) HashCommon.mix(key);
39 | }
40 |
41 | private static long key(int x, int z) {
42 | return ChunkPos.toLong(x, z);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/blend/ChunkPointGatherer.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.blend;
2 |
3 | import java.util.List;
4 |
5 | public class ChunkPointGatherer {
6 |
7 | private static final double CHUNK_RADIUS_RATIO = Math.sqrt(1.0 / 2.0);
8 |
9 | int halfChunkWidth;
10 | double maxPointContributionRadius;
11 | double maxPointContributionRadiusSq;
12 | double radiusPlusHalfChunkWidth;
13 | UnfilteredPointGatherer unfilteredPointGatherer;
14 |
15 | public ChunkPointGatherer(double frequency, double maxPointContributionRadius, int chunkWidth) {
16 | this.halfChunkWidth = chunkWidth / 2;
17 | this.maxPointContributionRadius = maxPointContributionRadius;
18 | this.maxPointContributionRadiusSq = maxPointContributionRadius * maxPointContributionRadius;
19 | this.radiusPlusHalfChunkWidth = maxPointContributionRadius + halfChunkWidth;
20 | unfilteredPointGatherer = new UnfilteredPointGatherer(frequency,
21 | maxPointContributionRadius + chunkWidth * CHUNK_RADIUS_RATIO);
22 | }
23 |
24 | public List> getPointsFromChunkBase(long seed, int chunkBaseWorldX, int chunkBaseWorldZ) {
25 | // Technically, the true minimum is between coordinates. But tests showed it was more efficient to add before converting to doubles.
26 | return getPointsFromChunkCenter(seed, chunkBaseWorldX + halfChunkWidth, chunkBaseWorldZ + halfChunkWidth);
27 | }
28 |
29 | public List> getPointsFromChunkCenter(long seed, int chunkCenterWorldX, int chunkCenterWorldZ) {
30 | List> worldPoints =
31 | unfilteredPointGatherer.getPoints(seed, chunkCenterWorldX, chunkCenterWorldZ);
32 | for (int i = 0; i < worldPoints.size(); i++) {
33 | GatheredPoint point = worldPoints.get(i);
34 |
35 | // Check if point contribution radius lies outside any coordinate in the chunk
36 | double axisCheckValueX = Math.abs(point.getX() - chunkCenterWorldX) - halfChunkWidth;
37 | double axisCheckValueZ = Math.abs(point.getZ() - chunkCenterWorldZ) - halfChunkWidth;
38 | if (axisCheckValueX >= maxPointContributionRadius || axisCheckValueZ >= maxPointContributionRadius
39 | || (axisCheckValueX > 0 && axisCheckValueZ > 0
40 | && axisCheckValueX*axisCheckValueX + axisCheckValueZ*axisCheckValueZ >= maxPointContributionRadiusSq)) {
41 |
42 | // If so, remove it.
43 | // Copy the last value to this value, and remove the last,
44 | // to avoid shifting because order doesn't matter.
45 | int lastIndex = worldPoints.size() - 1;
46 | worldPoints.set(i, worldPoints.get(lastIndex));
47 | worldPoints.remove(lastIndex);
48 | i--;
49 | }
50 | }
51 |
52 | return worldPoints;
53 | }
54 |
55 | }
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/blend/GatheredPoint.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.blend;
2 |
3 | public class GatheredPoint {
4 | private double x, z;
5 | private int hash;
6 | private T tag;
7 |
8 | public GatheredPoint(double x, double z, int hash) {
9 | this.x = x;
10 | this.z = z;
11 | this.hash = hash;
12 | }
13 |
14 | public double getX() {
15 | return x;
16 | }
17 |
18 | public double getZ() {
19 | return z;
20 | }
21 |
22 | public double getHash() {
23 | return hash;
24 | }
25 |
26 | public T getTag() {
27 | return tag;
28 | }
29 |
30 | public void setTag(T tag) {
31 | this.tag = tag;
32 | }
33 | }
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/blend/LinkedBiomeWeightMap.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.blend;
2 |
3 | public class LinkedBiomeWeightMap {
4 | private int biome;
5 | private double[] weights;
6 | private LinkedBiomeWeightMap next;
7 |
8 | public LinkedBiomeWeightMap(int biome, LinkedBiomeWeightMap next) {
9 | this.biome = biome;
10 | this.next = next;
11 | }
12 |
13 | public LinkedBiomeWeightMap(int biome, int chunkColumnCount, LinkedBiomeWeightMap next) {
14 | this.biome = biome;
15 | this.weights = new double[chunkColumnCount];
16 | this.next = next;
17 | }
18 |
19 | public int getBiome() {
20 | return biome;
21 | }
22 |
23 | public double[] getWeights() {
24 | return weights;
25 | }
26 |
27 | public void setWeights(double[] weights) {
28 | this.weights = weights;
29 | }
30 |
31 | public LinkedBiomeWeightMap getNext() {
32 | return next;
33 | }
34 | }
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/blend/ScatteredBiomeBlender.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.blend;
2 |
3 | import java.util.List;
4 |
5 | public class ScatteredBiomeBlender {
6 |
7 | private final int chunkColumnCount;
8 | private final int blendRadiusBoundArrayCenter;
9 | private final double chunkWidthMinusOne;
10 | private final double blendRadius, blendRadiusSq;
11 | private final double[] blendRadiusBound;
12 | private final ChunkPointGatherer gatherer;
13 |
14 | // chunkWidth should be a power of two.
15 | public ScatteredBiomeBlender(double samplingFrequency, double blendRadiusPadding, int chunkWidth) {
16 | this.chunkWidthMinusOne = chunkWidth - 1;
17 | this.chunkColumnCount = chunkWidth * chunkWidth;
18 | this.blendRadius = blendRadiusPadding + getInternalMinBlendRadiusForFrequency(samplingFrequency);
19 | this.blendRadiusSq = blendRadius * blendRadius;
20 | this.gatherer = new ChunkPointGatherer<>(samplingFrequency, blendRadius, chunkWidth);
21 |
22 | blendRadiusBoundArrayCenter = (int)Math.ceil(blendRadius) - 1;
23 | blendRadiusBound = new double[blendRadiusBoundArrayCenter * 2 + 1];
24 | for (int i = 0; i < blendRadiusBound.length; i++) {
25 | int dx = i - blendRadiusBoundArrayCenter;
26 | int maxDxBeforeTruncate = Math.abs(dx) + 1;
27 | blendRadiusBound[i] = Math.sqrt(blendRadiusSq - maxDxBeforeTruncate);
28 | }
29 |
30 | }
31 |
32 | public LinkedBiomeWeightMap getBlendForChunk(long seed, int chunkBaseWorldX, int chunkBaseWorldZ, BiomeEvaluationCallback callback) {
33 |
34 | // Get the list of data points in range.
35 | List> points = gatherer.getPointsFromChunkBase(seed, chunkBaseWorldX, chunkBaseWorldZ);
36 |
37 | // Evaluate and aggregate all biomes to be blended in this chunk.
38 | LinkedBiomeWeightMap linkedBiomeMapStartEntry = null;
39 | for (GatheredPoint point : points) {
40 |
41 | // Get the biome for this data point from the callback.
42 | int biome = callback.getBiomeAt(point.getX(), point.getZ());
43 |
44 | // Find or create the chunk biome blend weight layer entry for this biome.
45 | LinkedBiomeWeightMap entry = linkedBiomeMapStartEntry;
46 | while (entry != null) {
47 | if (entry.getBiome() == biome) break;
48 | entry = entry.getNext();
49 | }
50 | if (entry == null) {
51 | entry = linkedBiomeMapStartEntry =
52 | new LinkedBiomeWeightMap(biome, linkedBiomeMapStartEntry);
53 | }
54 |
55 | point.setTag(entry);
56 | }
57 |
58 | // If there is only one biome in range here, we can skip the actual blending step.
59 | if (linkedBiomeMapStartEntry != null && linkedBiomeMapStartEntry.getNext() == null) {
60 | double[] weights = new double[chunkColumnCount];
61 | linkedBiomeMapStartEntry.setWeights(weights);
62 | for (int i = 0; i < chunkColumnCount; i++) {
63 | weights[i] = 1.0;
64 | }
65 | return linkedBiomeMapStartEntry;
66 | }
67 |
68 | for (LinkedBiomeWeightMap entry = linkedBiomeMapStartEntry; entry != null; entry = entry.getNext()) {
69 | entry.setWeights(new double[chunkColumnCount]);
70 | }
71 |
72 | double z = chunkBaseWorldZ, x = chunkBaseWorldX;
73 | double xStart = x;
74 | double xEnd = xStart + chunkWidthMinusOne;
75 | for (int i = 0; i < chunkColumnCount; i++) {
76 |
77 | // Consider each data point to see if it's inside the radius for this column.
78 | double columnTotalWeight = 0.0;
79 | for (GatheredPoint point : points) {
80 | double dx = x - point.getX();
81 | double dz = z - point.getZ();
82 |
83 | double distSq = dx * dx + dz * dz;
84 |
85 | // If it's inside the radius...
86 | if (distSq < blendRadiusSq) {
87 |
88 | // Relative weight = [r^2 - (x^2 + z^2)]^2
89 | double weight = blendRadiusSq - distSq;
90 | weight *= weight;
91 |
92 | point.getTag().getWeights()[i] += weight;
93 | columnTotalWeight += weight;
94 | }
95 | }
96 |
97 | // Normalize so all weights in a column add up to 1.
98 | double inverseTotalWeight = 1.0 / columnTotalWeight;
99 | for (LinkedBiomeWeightMap entry = linkedBiomeMapStartEntry; entry != null; entry = entry.getNext()) {
100 | entry.getWeights()[i] *= inverseTotalWeight;
101 | }
102 |
103 | // A double can fully represent an int, so no precision loss to worry about here.
104 | if (x == xEnd) {
105 | x = xStart;
106 | z++;
107 | } else x++;
108 | }
109 |
110 | return linkedBiomeMapStartEntry;
111 | }
112 |
113 | public static double getInternalMinBlendRadiusForFrequency(double samplingFrequency) {
114 | return UnfilteredPointGatherer.MAX_GRIDSCALE_DISTANCE_TO_CLOSEST_POINT / samplingFrequency;
115 | }
116 |
117 | public double getInternalBlendRadius() {
118 | return blendRadius;
119 | }
120 |
121 | @FunctionalInterface
122 | public static interface BiomeEvaluationCallback {
123 | int getBiomeAt(double x, double z);
124 | }
125 |
126 | private static class BiomeEvaluation {
127 | int biome;
128 | double tempDzSquared;
129 | public BiomeEvaluation(int biome) {
130 | this.biome = biome;
131 | }
132 | }
133 |
134 | }
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/gen/ContinentCache.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.gen;
2 |
3 | import it.unimi.dsi.fastutil.HashCommon;
4 | import net.minecraft.util.math.ChunkPos;
5 | import supercoder79.simplexterrain.api.Heightmap;
6 | import supercoder79.simplexterrain.noise.gradient.OpenSimplexNoise;
7 |
8 | import java.util.Arrays;
9 |
10 | public class ContinentCache {
11 | private final long[] keys;
12 | private final int[] values;
13 |
14 | private final OpenSimplexNoise noise;
15 | private final int seaLevel;
16 |
17 | public ContinentCache(long seed, int seaLevel) {
18 | this.noise = new OpenSimplexNoise(seed);
19 | this.seaLevel = seaLevel;
20 |
21 | this.keys = new long[2048];
22 | this.values = new int[2048];
23 |
24 | Arrays.fill(this.keys, Long.MIN_VALUE);
25 | }
26 |
27 | public int get(int x, int z) {
28 | long key = key(x, z);
29 | int idx = hash(key) & 2047;
30 |
31 | if (this.keys[idx] == key) {
32 | return this.values[idx];
33 | }
34 |
35 | int height = (int) (this.seaLevel + (noise.sample(x / 1200.0, z / 1200.0) + 0.2) * 16);
36 | this.values[idx] = height;
37 | this.keys[idx] = key;
38 |
39 | return height;
40 | }
41 |
42 | private static int hash(long key) {
43 | return (int) HashCommon.mix(key);
44 | }
45 |
46 | private static long key(int x, int z) {
47 | return ChunkPos.toLong(x, z);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/gen/ContinentGenerator.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.gen;
2 |
3 | import supercoder79.simplexterrain.api.Heightmap;
4 |
5 | public class ContinentGenerator implements Heightmap {
6 | private final ThreadLocal cache;
7 |
8 | public ContinentGenerator(long seed, int seaLevel) {
9 | this.cache = ThreadLocal.withInitial(() -> new ContinentCache(seed, seaLevel));
10 | }
11 |
12 | @Override
13 | public int getHeight(int x, int z) {
14 | return this.cache.get().get(x, z);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/gen/SimplexBiomeSource.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.gen;
2 |
3 | import com.mojang.serialization.Codec;
4 | import com.mojang.serialization.codecs.RecordCodecBuilder;
5 | import net.minecraft.util.registry.Registry;
6 | import net.minecraft.util.registry.RegistryKey;
7 | import net.minecraft.util.registry.RegistryLookupCodec;
8 | import net.minecraft.world.biome.Biome;
9 | import net.minecraft.world.biome.BiomeKeys;
10 | import net.minecraft.world.biome.BuiltinBiomes;
11 | import net.minecraft.world.biome.source.BiomeLayerSampler;
12 | import net.minecraft.world.biome.source.BiomeSource;
13 | import net.minecraft.world.biome.source.VanillaLayeredBiomeSource;
14 | import net.minecraft.world.gen.ChunkRandom;
15 | import supercoder79.simplexterrain.SimplexTerrain;
16 | import supercoder79.simplexterrain.api.BackingBiomeSource;
17 | import supercoder79.simplexterrain.api.Heightmap;
18 | import supercoder79.simplexterrain.world.BiomeData;
19 | import supercoder79.simplexterrain.world.biomelayers.SimplexBiomeLayers;
20 | import supercoder79.simplexterrain.world.noisetype.NoiseType;
21 | import supercoder79.simplexterrain.world.noisetype.NoiseTypeHolder;
22 |
23 | public class SimplexBiomeSource extends BiomeSource implements BackingBiomeSource {
24 | public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group(
25 | RegistryLookupCodec.of(Registry.BIOME_KEY).forGetter(source -> source.biomeRegistry),
26 | Codec.LONG.fieldOf("seed").stable().forGetter(source -> source.seed))
27 | .apply(instance, instance.stable(SimplexBiomeSource::new)));
28 |
29 | private final BiomeLayerSampler backingSampler;
30 |
31 | private final Registry biomeRegistry;
32 | private final long seed;
33 |
34 | private Heightmap heightmap = Heightmap.NONE;
35 | private Heightmap continent = Heightmap.NONE;
36 |
37 | public SimplexBiomeSource(Registry biomeRegistry, long seed) {
38 | super(VanillaLayeredBiomeSource.BIOMES.stream().map((registryKey) -> () -> (Biome)biomeRegistry.getOrThrow(registryKey)));
39 | this.biomeRegistry = biomeRegistry;
40 | this.seed = seed;
41 |
42 | this.backingSampler = SimplexBiomeLayers.build(biomeRegistry, seed);
43 | NoiseTypeHolder.initialize(new ChunkRandom(seed));
44 | }
45 |
46 | public void setHeightmap(Heightmap heightmap) {
47 | this.heightmap = heightmap;
48 | }
49 |
50 | public void setContinentHeightmap(Heightmap heightmap) {
51 | this.continent = heightmap;
52 | }
53 |
54 | @Override
55 | public Biome getBiomeForNoiseGen(int x, int y, int z) {
56 | if (heightmap == null) return BuiltinBiomes.PLAINS;
57 | Biome biome = getBiomeAt(x, z, heightmap.getBiomeData(x, z));
58 | return biome == null ? BuiltinBiomes.PLAINS : biome;
59 | }
60 |
61 | public Biome getBiomeAt(int x, int z, BiomeData data) {
62 | int height = data.getHeight();
63 |
64 | int continent = this.continent.getHeight(x << 2, z << 2);
65 |
66 | if (continent < SimplexTerrain.CONFIG.seaLevel) {
67 | return this.biomeRegistry.get(BiomeKeys.OCEAN);
68 | }
69 |
70 | RegistryKey key = this.biomeRegistry.getKey(this.backingSampler.sample(this.biomeRegistry, x << 2, z << 2)).get();
71 |
72 | NoiseType type = NoiseTypeHolder.get(key).get(x << 2, z << 2);
73 |
74 | // TODO: provide a better way of doing this
75 | int y = (int) type.modify(x << 2, z << 2, 0, 1, new BiomeData());
76 |
77 | return this.biomeRegistry.get(type.modifyBiome(y, key));
78 | }
79 |
80 | @Override
81 | protected Codec extends BiomeSource> getCodec() {
82 | return CODEC;
83 | }
84 |
85 | @Override
86 | public BiomeSource withSeed(long l) {
87 | return new SimplexBiomeSource(this.biomeRegistry, l);
88 | }
89 |
90 | @Override
91 | public RegistryKey getBacking(int x, int z) {
92 | return this.biomeRegistry.getKey(this.backingSampler.sample(this.biomeRegistry, x, z)).get();
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/gen/SimplexNetherGeneration.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.gen;
2 |
3 | import net.minecraft.block.Blocks;
4 | import net.minecraft.util.math.BlockPos;
5 | import net.minecraft.world.WorldAccess;
6 | import net.minecraft.world.biome.Biome;
7 | import net.minecraft.world.biome.source.BiomeSource;
8 | import net.minecraft.world.chunk.Chunk;
9 | import supercoder79.simplexterrain.noise.gradient.SimplexStyleNoise;
10 |
11 | public class SimplexNetherGeneration {
12 |
13 | private static SimplexStyleNoise mainNoise;
14 | private static SimplexStyleNoise detailsNoise;
15 |
16 | public static void init(long seed) {
17 | mainNoise = new SimplexStyleNoise(seed + 12);
18 | detailsNoise = new SimplexStyleNoise(seed + 20);
19 | }
20 |
21 | public static void generate(WorldAccess world, Chunk chunk, BiomeSource biomeSource, int seaLevel) {
22 | //TODO: threading
23 |
24 | BlockPos.Mutable mutable = new BlockPos.Mutable();
25 |
26 | for (int x = 0; x < 16; x++) {
27 | mutable.setX(x);
28 |
29 | for (int z = 0; z < 16; z++) {
30 | mutable.setZ(z);
31 |
32 | double depth = 0;
33 | double scale = 0;
34 |
35 | for (int x1 = -1; x1 <= 1; x1++) {
36 | for (int z1 = -1; z1 <= 1; z1++) {
37 | Biome biome = biomeSource.getBiomeForNoiseGen((chunk.getPos().x*16) + (x + x1), 32, (chunk.getPos().z*16) + (z + z1));
38 |
39 | depth += biome.getDepth();
40 | scale += biome.getScale();
41 | }
42 | }
43 |
44 | depth /= 9;
45 | scale /= 9;
46 |
47 | for (int y = 0; y < 127; y++) {
48 | mutable.setY(y);
49 | if (getNoiseAt(depth, scale, (chunk.getPos().x*16) + x, y, (chunk.getPos().z*16) + z) > 0) {
50 | chunk.setBlockState(mutable, Blocks.NETHERRACK.getDefaultState(), false);
51 | } else if (y < seaLevel) {
52 | chunk.setBlockState(mutable, Blocks.LAVA.getDefaultState(), false);
53 | }
54 | }
55 | }
56 | }
57 | }
58 |
59 | private static double getNoiseAt(double depth, double scale, int x, int y, int z) {
60 | double noise = mainNoise.sample(x / 80.0, y / 60.0, z / 80.0) * (1 + depth);
61 | noise += detailsNoise.sample(x / 24.0, y / 18.0, z / 24.0) * 0.125;
62 |
63 | noise /= (1 - scale);
64 |
65 | noise += Math.max((22.0 / y) - 1, 0); // lower bound
66 | noise += Math.max((-22.0 / (y - 130)) - 1, 0); // upper bound
67 |
68 | return noise;
69 | }
70 | }
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/noisemodifier/DetailNoiseModifier.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.noisemodifier;
2 |
3 | import net.minecraft.world.gen.ChunkRandom;
4 | import supercoder79.simplexterrain.SimplexTerrain;
5 | import supercoder79.simplexterrain.api.noise.Noise;
6 | import supercoder79.simplexterrain.api.noise.OctaveNoiseSampler;
7 | import supercoder79.simplexterrain.api.noisemodifier.NoiseModifier;
8 | import supercoder79.simplexterrain.configs.ConfigHelper;
9 | import supercoder79.simplexterrain.configs.noisemodifiers.DetailsConfigData;
10 | import supercoder79.simplexterrain.world.BiomeData;
11 |
12 | import java.nio.file.Paths;
13 |
14 | public class DetailNoiseModifier implements NoiseModifier {
15 | private DetailsConfigData config;
16 | private OctaveNoiseSampler extends Noise> detailNoise;
17 |
18 | @Override
19 | public void init(long seed) {
20 | detailNoise = new OctaveNoiseSampler<>(SimplexTerrain.CONFIG.noiseGenerator.noiseClass, new ChunkRandom(seed), config.octaves, config.frequency, config.amplitudeHigh, config.amplitudeLow);
21 | }
22 |
23 | @Override
24 | public void setup() {
25 | config = ConfigHelper.getFromConfig(DetailsConfigData.class, Paths.get("config", "simplexterrain", "noisemodifiers", "details.json"));
26 | }
27 |
28 | @Override
29 | public double modify(int x, int z, double currentNoiseValue, BiomeData data) {
30 | return currentNoiseValue + detailNoise.sample(x, z);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/noisemodifier/MountainsNoiseModifier.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.noisemodifier;
2 |
3 | import net.minecraft.world.gen.ChunkRandom;
4 | import supercoder79.simplexterrain.SimplexTerrain;
5 | import supercoder79.simplexterrain.api.noise.Noise;
6 | import supercoder79.simplexterrain.api.noise.OctaveNoiseSampler;
7 | import supercoder79.simplexterrain.api.noisemodifier.NoiseModifier;
8 | import supercoder79.simplexterrain.configs.ConfigHelper;
9 | import supercoder79.simplexterrain.configs.noisemodifiers.MountainConfigData;
10 | import supercoder79.simplexterrain.world.BiomeData;
11 |
12 | import java.nio.file.Paths;
13 |
14 | public class MountainsNoiseModifier implements NoiseModifier {
15 | private MountainConfigData config;
16 | private OctaveNoiseSampler extends Noise> mountainNoise;
17 |
18 | @Override
19 | public void init(long seed) {
20 | mountainNoise = new OctaveNoiseSampler<>(SimplexTerrain.CONFIG.noiseGenerator.noiseClass, new ChunkRandom(seed + 20), config.octaves, config.frequency, config.amplitudeHigh, config.amplitudeLow);
21 | }
22 |
23 | @Override
24 | public void setup() {
25 | config = ConfigHelper.getFromConfig(MountainConfigData.class, Paths.get("config", "simplexterrain", "noisemodifiers", "mountains.json"));
26 | }
27 |
28 | @Override
29 | public double modify(int x, int z, double currentNoiseValue, BiomeData data) {
30 | return currentNoiseValue + Math.max(mountainNoise.sample(x, z), 0);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/noisemodifier/MushroomIslandsNoiseModifier.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.noisemodifier;
2 |
3 | import net.minecraft.util.math.MathHelper;
4 | import supercoder79.simplexterrain.SimplexTerrain;
5 | import supercoder79.simplexterrain.api.noise.Noise;
6 | import supercoder79.simplexterrain.api.noisemodifier.NoiseModifier;
7 | import supercoder79.simplexterrain.configs.ConfigHelper;
8 | import supercoder79.simplexterrain.configs.noisemodifiers.MushroomFieldsConfigData;
9 | import supercoder79.simplexterrain.noise.NoiseMath;
10 | import supercoder79.simplexterrain.noise.gradient.OpenSimplexNoise;
11 | import supercoder79.simplexterrain.world.BiomeData;
12 |
13 | import java.nio.file.Paths;
14 |
15 | public class MushroomIslandsNoiseModifier implements NoiseModifier {
16 | private Noise maskNoise;
17 | private MushroomFieldsConfigData config;
18 |
19 | @Override
20 | public void init(long seed) {
21 | this.maskNoise = new OpenSimplexNoise(seed - 146);
22 | }
23 |
24 | @Override
25 | public void setup() {
26 | config = ConfigHelper.getFromConfig(MushroomFieldsConfigData.class, Paths.get("config", "simplexterrain", "noisemodifiers", "mushroom_islands.json"));
27 | }
28 |
29 | @Override
30 | public double modify(int x, int z, double currentNoiseValue, BiomeData data) {
31 | double mNoise = maskNoise.sample(x / config.scale, z / config.scale);
32 | if (mNoise > config.threshold) {
33 | double ndelta = (mNoise - config.threshold) * 50;
34 | double add = MathHelper.clampedLerp(0, config.height, ndelta);
35 | double delta = (SimplexTerrain.CONFIG.seaLevel - 15) - NoiseMath.sigmoid(currentNoiseValue);
36 |
37 | if (delta > 0) {
38 | double end = MathHelper.clampedLerp(0, add, delta / 1.5);
39 |
40 | currentNoiseValue += end;
41 | data.setMushroomIsland();
42 | }
43 | }
44 |
45 | return currentNoiseValue;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/noisemodifier/NoiseModifiers.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.noisemodifier;
2 |
3 | import supercoder79.simplexterrain.api.noisemodifier.NoiseModifier;
4 |
5 | public enum NoiseModifiers {
6 | MOUNTAINS(new MountainsNoiseModifier()),
7 | RIDGES(new RidgesNoiseModifier()),
8 | DETAILS(new DetailNoiseModifier()),
9 | PLATEAUS(new PlateausNoiseModifier()),
10 | RIVERS(new RiversNoiseModifier()),
11 | MUSHROOM_ISLANDS(new MushroomIslandsNoiseModifier());
12 |
13 | public NoiseModifier noiseModifier;
14 |
15 | NoiseModifiers(NoiseModifier noiseModifier) {
16 | this.noiseModifier = noiseModifier;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/noisemodifier/PlateausNoiseModifier.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.noisemodifier;
2 |
3 | import net.minecraft.util.math.MathHelper;
4 | import net.minecraft.world.gen.ChunkRandom;
5 | import supercoder79.simplexterrain.SimplexTerrain;
6 | import supercoder79.simplexterrain.api.noise.Noise;
7 | import supercoder79.simplexterrain.api.noise.OctaveNoiseSampler;
8 | import supercoder79.simplexterrain.api.noisemodifier.NoiseModifier;
9 | import supercoder79.simplexterrain.configs.ConfigHelper;
10 | import supercoder79.simplexterrain.configs.noisemodifiers.PlateausConfigData;
11 | import supercoder79.simplexterrain.world.BiomeData;
12 |
13 | import java.nio.file.Paths;
14 |
15 | public class PlateausNoiseModifier implements NoiseModifier {
16 | private PlateausConfigData config;
17 | private OctaveNoiseSampler extends Noise> plateauNoise;
18 |
19 | @Override
20 | public void init(long seed) {
21 | plateauNoise = new OctaveNoiseSampler<>(SimplexTerrain.CONFIG.noiseGenerator.noiseClass, new ChunkRandom(seed + 3), config.octaves, config.frequency, 1, 1);
22 | }
23 |
24 | @Override
25 | public void setup() {
26 | config = ConfigHelper.getFromConfig(PlateausConfigData.class, Paths.get("config", "simplexterrain", "noisemodifiers", "plateaus.json"));
27 | }
28 |
29 | @Override
30 | public double modify(int x, int z, double currentNoiseValue, BiomeData data) {
31 | double noise = plateauNoise.sample(x, z);
32 |
33 | if (noise > config.threshold) {
34 | currentNoiseValue += MathHelper.lerp(fade(noise, config.threshold, config.interpolation), 0, config.height);
35 | }
36 |
37 | return currentNoiseValue;
38 | }
39 |
40 | private static double fade(double noise, double threshold, double interpolation) {
41 | double fade = (threshold + interpolation) - noise;
42 | // Create a smooth interpolation
43 | if (fade > 0) {
44 | fade = Math.abs(fade);
45 | fade /= interpolation;
46 | fade = smoothstep(1 - fade);
47 | } else {
48 | // This is to make the rest of the plateau flat-ish (outside of the interpolation region)
49 | fade = 1;
50 | }
51 |
52 | return fade;
53 | }
54 |
55 | private static double smoothstep(double d) {
56 | return d * d * (3 - 2 * d);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/noisemodifier/RidgesNoiseModifier.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.noisemodifier;
2 |
3 | import net.minecraft.world.gen.ChunkRandom;
4 | import supercoder79.simplexterrain.SimplexTerrain;
5 | import supercoder79.simplexterrain.api.noise.Noise;
6 | import supercoder79.simplexterrain.api.noise.OctaveNoiseSampler;
7 | import supercoder79.simplexterrain.api.noisemodifier.NoiseModifier;
8 | import supercoder79.simplexterrain.configs.ConfigHelper;
9 | import supercoder79.simplexterrain.configs.noisemodifiers.RidgesConfigData;
10 | import supercoder79.simplexterrain.world.BiomeData;
11 |
12 | import java.nio.file.Paths;
13 |
14 | public class RidgesNoiseModifier implements NoiseModifier {
15 | private RidgesConfigData config;
16 | private OctaveNoiseSampler extends Noise> ridgedNoise;
17 |
18 | @Override
19 | public void init(long seed) {
20 | ridgedNoise = new OctaveNoiseSampler<>(SimplexTerrain.CONFIG.noiseGenerator.noiseClass, new ChunkRandom(seed - 20), config.octaves, config.frequency, 1, 1);
21 | }
22 |
23 | @Override
24 | public void setup() {
25 | config = ConfigHelper.getFromConfig(RidgesConfigData.class, Paths.get("config", "simplexterrain", "noisemodifiers", "ridges.json"));
26 | }
27 |
28 | @Override
29 | public double modify(int x, int z, double currentNoiseValue, BiomeData data) {
30 | return currentNoiseValue + ((1 - Math.abs(ridgedNoise.sample(x, z))) * config.amplitude);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/noisemodifier/RiversNoiseModifier.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.noisemodifier;
2 |
3 | import net.minecraft.util.math.MathHelper;
4 | import supercoder79.simplexterrain.SimplexTerrain;
5 | import supercoder79.simplexterrain.api.noise.Noise;
6 | import supercoder79.simplexterrain.api.noise.OctaveNoiseSampler;
7 | import supercoder79.simplexterrain.api.noisemodifier.NoiseModifier;
8 | import supercoder79.simplexterrain.configs.ConfigHelper;
9 | import supercoder79.simplexterrain.configs.noisemodifiers.RiversConfigData;
10 | import supercoder79.simplexterrain.noise.NoiseMath;
11 | import supercoder79.simplexterrain.noise.gradient.OpenSimplexNoise;
12 | import supercoder79.simplexterrain.world.BiomeData;
13 |
14 | import java.nio.file.Paths;
15 | import java.util.Random;
16 |
17 | public class RiversNoiseModifier implements NoiseModifier {
18 | public RiversConfigData config;
19 | private Noise noise;
20 | private Noise detailNoise;
21 | private Noise depthNoise;
22 | private Noise sizeNoise;
23 |
24 | @Override
25 | public void init(long seed) {
26 | noise = new OpenSimplexNoise(seed - 43);
27 | detailNoise = new OpenSimplexNoise(seed + 43);
28 | depthNoise = new OctaveNoiseSampler<>(OpenSimplexNoise.class, new Random(seed + 32), 3, config.scale / 2.0, 2.5, 6);
29 | sizeNoise = new OpenSimplexNoise(seed + 53);
30 | }
31 |
32 | @Override
33 | public void setup() {
34 | config = ConfigHelper.getFromConfig(RiversConfigData.class, Paths.get("config", "simplexterrain", "noisemodifiers", "rivers.json"));
35 | }
36 |
37 | @Override
38 | public double modify(int x, int z, double currentNoiseValue, BiomeData data) {
39 | double noise = this.noise.sample(x / config.scale, z / config.scale) + (detailNoise.sample(x / config.scale * 5.0, z / config.scale * 5.0) * 0.2);
40 |
41 |
42 | double depth = config.depth + depthNoise.sample(x, z);
43 | double size = config.size + (sizeNoise.sample(x / config.scale * 3.0, z / config.scale * 3.0) * 0.025);
44 |
45 | if (currentNoiseValue > depth) {
46 | if (noise < size && noise > -size) {
47 | // Interpolate downwards
48 | currentNoiseValue = MathHelper.lerp(smoothstep(noise / size), currentNoiseValue, depth);
49 |
50 | double realY = NoiseMath.sigmoid(currentNoiseValue);
51 |
52 | // Place a river if we're less than sea level, otherwise force place lowlands to prevent beach gen
53 | if (realY < SimplexTerrain.CONFIG.seaLevel) {
54 | data.setRiver();
55 | } else if (realY <= SimplexTerrain.CONFIG.seaLevel + 9) {
56 | data.setForcedLowlands();
57 | }
58 | }
59 | }
60 |
61 | return currentNoiseValue;
62 | }
63 | public static double smoothstep(double t) {
64 | return (1 - t * t) * (1 - t * t) * (1 - t * t);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/noisetype/DefaultNoiseType.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.noisetype;
2 |
3 | import net.minecraft.world.gen.ChunkRandom;
4 | import supercoder79.simplexterrain.world.BiomeData;
5 |
6 | public class DefaultNoiseType implements NoiseType {
7 | public static final DefaultNoiseType INSTANCE = new DefaultNoiseType();
8 |
9 | @Override
10 | public void init(ChunkRandom random) {
11 |
12 | }
13 |
14 | @Override
15 | public double modify(int x, int z, double currentNoiseValue, double weight, BiomeData data) {
16 | return 0;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/noisetype/NoiseType.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.noisetype;
2 |
3 | import net.minecraft.util.math.Vec3d;
4 | import net.minecraft.util.registry.RegistryKey;
5 | import net.minecraft.world.biome.Biome;
6 | import net.minecraft.world.gen.ChunkRandom;
7 | import supercoder79.simplexterrain.world.BiomeData;
8 |
9 | import java.util.Map;
10 |
11 | public interface NoiseType {
12 | void init(ChunkRandom random);
13 |
14 | double modify(int x, int z, double currentNoiseValue, double weight, BiomeData data);
15 |
16 | default RegistryKey modifyBiome(int y, RegistryKey existing) {
17 | return existing;
18 | }
19 |
20 | default void addToPicker(Map points, double theta) {
21 | points.put(new Vec3d(Math.cos(theta) * Math.sqrt(2), 0, Math.sin(theta) * Math.sqrt(2)), this);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/noisetype/NoiseTypeCache.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.noisetype;
2 |
3 | import it.unimi.dsi.fastutil.HashCommon;
4 | import net.minecraft.util.math.ChunkPos;
5 |
6 | import java.util.Arrays;
7 |
8 | public class NoiseTypeCache {
9 | private final NoiseTypePicker picker;
10 | private final ThreadLocal cache;
11 |
12 | public NoiseTypeCache(NoiseTypePicker picker) {
13 | this.picker = picker;
14 | this.cache = ThreadLocal.withInitial(() -> new Cache(this.picker));
15 | }
16 |
17 | public NoiseTypePicker getPicker() {
18 | return picker;
19 | }
20 |
21 | public NoiseType get(int x, int z) {
22 | return this.cache.get().get(x, z);
23 | }
24 |
25 | private static class Cache {
26 | private final long[] keys;
27 | private final NoiseType[] values;
28 | private final NoiseTypePicker picker;
29 |
30 | private Cache(NoiseTypePicker picker) {
31 | this.picker = picker;
32 | this.keys = new long[2048];
33 | this.values = new NoiseType[2048];
34 |
35 | Arrays.fill(this.keys, Long.MIN_VALUE);
36 | }
37 |
38 | public NoiseType get(int x, int z) {
39 | long key = key(x, z);
40 | int idx = hash(key) & 2047;
41 |
42 | if (this.keys[idx] == key) {
43 | return this.values[idx];
44 | }
45 |
46 | NoiseType type = this.picker.get(x, z);
47 | this.values[idx] = type;
48 | this.keys[idx] = key;
49 |
50 | return type;
51 | }
52 |
53 | private static int hash(long key) {
54 | return (int) HashCommon.mix(key);
55 | }
56 |
57 | private static long key(int x, int z) {
58 | return ChunkPos.toLong(x, z);
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/noisetype/NoiseTypeHolder.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.noisetype;
2 |
3 | import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
4 | import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
5 | import net.minecraft.util.registry.Registry;
6 | import net.minecraft.util.registry.RegistryKey;
7 | import net.minecraft.world.biome.Biome;
8 | import net.minecraft.world.gen.ChunkRandom;
9 |
10 | import java.util.HashMap;
11 | import java.util.LinkedHashMap;
12 | import java.util.Map;
13 | import java.util.function.Function;
14 |
15 | public class NoiseTypeHolder {
16 | public static final Map, Function> BIOME_FACTORIES = new LinkedHashMap<>();
17 | public static final Map IDS = new Reference2IntOpenHashMap<>();
18 | public static final Map IDS_REVERSE = new Int2ReferenceOpenHashMap<>();
19 | public static int currentId = 0; // TODO: remove ugly shared state
20 |
21 | private static final Map, NoiseTypeCache> FOR_BIOME = new LinkedHashMap<>();
22 |
23 | public static void initialize(ChunkRandom random) {
24 | FOR_BIOME.clear();
25 | IDS.clear();
26 | currentId = 0;
27 |
28 | for (Map.Entry, Function> entry : BIOME_FACTORIES.entrySet()) {
29 | FOR_BIOME.put(entry.getKey(), entry.getValue().apply(random));
30 | }
31 | }
32 |
33 | public static NoiseTypeCache get(RegistryKey biome) {
34 | return FOR_BIOME.get(biome);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/noisetype/NoiseTypePicker.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.noisetype;
2 |
3 | import net.minecraft.util.math.Vec3d;
4 | import supercoder79.simplexterrain.noise.gradient.OpenSimplexNoise;
5 |
6 | import java.util.*;
7 |
8 | public class NoiseTypePicker {
9 | private final OpenSimplexNoise noise1;
10 | private final OpenSimplexNoise noise2;
11 | private final OpenSimplexNoise noise3;
12 | private final OpenSimplexNoise noise1a;
13 | private final OpenSimplexNoise noise3a;
14 | private final Map points;
15 |
16 | public NoiseTypePicker(Random random, List types) {
17 | this.noise1 = new OpenSimplexNoise(random.nextLong());
18 | this.noise2 = new OpenSimplexNoise(random.nextLong());
19 | this.noise3 = new OpenSimplexNoise(random.nextLong());
20 | this.noise1a = new OpenSimplexNoise(random.nextLong());
21 | this.noise3a = new OpenSimplexNoise(random.nextLong());
22 |
23 |
24 | Map points = new HashMap<>();
25 | for (int i = 0; i < types.size(); i++) {
26 | // Add to registry
27 | NoiseTypeHolder.IDS.put(types.get(i), NoiseTypeHolder.currentId);
28 | NoiseTypeHolder.IDS_REVERSE.put(NoiseTypeHolder.currentId, types.get(i));
29 | NoiseTypeHolder.currentId++;
30 |
31 | double theta = (i / (double)types.size()) * Math.PI * 2;
32 | types.get(i).addToPicker(points, theta);
33 | }
34 |
35 | this.points = points;
36 | }
37 |
38 | public Map getPoints() {
39 | return points;
40 | }
41 |
42 | public NoiseType get(int x, int z) {
43 | double ax = this.noise1.sample( x / 600.0, z / 600.0) + (this.noise1a.sample(x / 200.0, z / 200.0) * 0.333);
44 | double ay = this.noise2.sample( x / 600.0, z / 600.0);
45 | double az = this.noise3.sample(x / 600.0, z / 600.0) + (this.noise3a.sample(x / 200.0, z / 200.0) * 0.333);
46 |
47 | Vec3d vec = new Vec3d(ax, ay, az);
48 | double minimumDist = Double.MAX_VALUE;
49 | NoiseType closest = DefaultNoiseType.INSTANCE;
50 |
51 | for (Map.Entry type : this.points.entrySet()) {
52 | double dist = type.getKey().squaredDistanceTo(vec);
53 | if (dist < minimumDist) {
54 | minimumDist = dist;
55 | closest = type.getValue();
56 | }
57 | }
58 |
59 | return closest;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/noisetype/desert/DunesNoiseType.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.noisetype.desert;
2 |
3 | import net.minecraft.world.gen.ChunkRandom;
4 | import supercoder79.simplexterrain.api.noise.OctaveNoiseSampler;
5 | import supercoder79.simplexterrain.noise.gradient.OpenSimplexNoise;
6 | import supercoder79.simplexterrain.world.BiomeData;
7 | import supercoder79.simplexterrain.world.noisetype.NoiseType;
8 |
9 | public class DunesNoiseType implements NoiseType {
10 | private OpenSimplexNoise ridged;
11 | private OctaveNoiseSampler noise;
12 |
13 | @Override
14 | public void init(ChunkRandom random) {
15 | this.ridged = new OpenSimplexNoise(random.nextLong());
16 | this.noise = new OctaveNoiseSampler<>(OpenSimplexNoise.class, random, 2, 300, 20, 10);
17 | }
18 |
19 | @Override
20 | public double modify(int x, int z, double currentNoiseValue, double weight, BiomeData data) {
21 | double ridgedNoise = (1 - Math.abs(this.ridged.sample(x / 120.0, z / 120.0))) * 20 * weight;
22 |
23 | return ridgedNoise + this.noise.sample(x, z) * weight;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/noisetype/desert/SavannaHillsNoiseType.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.noisetype.desert;
2 |
3 | import net.minecraft.util.math.MathHelper;
4 | import net.minecraft.util.registry.RegistryKey;
5 | import net.minecraft.world.biome.Biome;
6 | import net.minecraft.world.biome.BiomeKeys;
7 | import net.minecraft.world.gen.ChunkRandom;
8 | import supercoder79.simplexterrain.api.noise.OctaveNoiseSampler;
9 | import supercoder79.simplexterrain.noise.gradient.OpenSimplexNoise;
10 | import supercoder79.simplexterrain.world.BiomeData;
11 | import supercoder79.simplexterrain.world.noisetype.NoiseType;
12 |
13 | public class SavannaHillsNoiseType implements NoiseType {
14 | private OctaveNoiseSampler noise;
15 | private OpenSimplexNoise ridged;
16 |
17 | @Override
18 | public void init(ChunkRandom random) {
19 | this.noise = new OctaveNoiseSampler<>(OpenSimplexNoise.class, random, 3, 220, 30, 16);
20 | this.ridged = new OpenSimplexNoise(random.nextLong());
21 | }
22 |
23 | @Override
24 | public double modify(int x, int z, double currentNoiseValue, double weight, BiomeData data) {
25 | double ridgedNoise = Math.abs(1 - this.ridged.sample(x / 100.0, z / 100.0)) * 12 * weight;
26 | double addition = MathHelper.lerp(MathHelper.perlinFade(weight), 0, 10);
27 | double noise = this.noise.sample(x, z) * weight;
28 |
29 | return addition + noise + ridgedNoise;
30 | }
31 |
32 | @Override
33 | public RegistryKey modifyBiome(int y, RegistryKey existing) {
34 | return y > (85 - 61) ? BiomeKeys.SAVANNA : existing;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/noisetype/forest/HillsNoiseType.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.noisetype.forest;
2 |
3 | import net.minecraft.world.gen.ChunkRandom;
4 | import supercoder79.simplexterrain.api.noise.OctaveNoiseSampler;
5 | import supercoder79.simplexterrain.noise.gradient.OpenSimplexNoise;
6 | import supercoder79.simplexterrain.world.BiomeData;
7 | import supercoder79.simplexterrain.world.noisetype.NoiseType;
8 |
9 | public class HillsNoiseType implements NoiseType {
10 | private OctaveNoiseSampler noise;
11 |
12 | @Override
13 | public void init(ChunkRandom random) {
14 | this.noise = new OctaveNoiseSampler<>(OpenSimplexNoise.class, random, 3, 120, 20, 12);
15 | }
16 |
17 | @Override
18 | public double modify(int x, int z, double currentNoiseValue, double weight, BiomeData data) {
19 | return (8 + noise.sample(x, z)) * weight;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/noisetype/plains/ForestedHillsNoiseType.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.noisetype.plains;
2 |
3 | import net.minecraft.util.math.MathHelper;
4 | import net.minecraft.util.registry.RegistryKey;
5 | import net.minecraft.world.biome.Biome;
6 | import net.minecraft.world.biome.BiomeKeys;
7 | import net.minecraft.world.gen.ChunkRandom;
8 | import supercoder79.simplexterrain.api.noise.OctaveNoiseSampler;
9 | import supercoder79.simplexterrain.noise.gradient.OpenSimplexNoise;
10 | import supercoder79.simplexterrain.world.BiomeData;
11 | import supercoder79.simplexterrain.world.noisetype.NoiseType;
12 |
13 | public class ForestedHillsNoiseType implements NoiseType {
14 | private OctaveNoiseSampler noise;
15 | private OpenSimplexNoise ridged;
16 |
17 | @Override
18 | public void init(ChunkRandom random) {
19 | this.noise = new OctaveNoiseSampler<>(OpenSimplexNoise.class, random, 4, 160, 30, 16);
20 | this.ridged = new OpenSimplexNoise(random.nextLong());
21 | }
22 |
23 | @Override
24 | public double modify(int x, int z, double currentNoiseValue, double weight, BiomeData data) {
25 | double ridgedNoise = Math.abs(1 - this.ridged.sample(x / 60.0, z / 60.0)) * 12 * weight;
26 | double addition = MathHelper.lerp(MathHelper.perlinFade(weight), 0, 10);
27 | double noise = this.noise.sample(x, z) * weight;
28 |
29 | return addition + noise + ridgedNoise;
30 | }
31 |
32 | @Override
33 | public RegistryKey modifyBiome(int y, RegistryKey existing) {
34 | return y > (85 - 61) ? BiomeKeys.FOREST : existing;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/noisetype/plains/LowLyingPlainsNoiseType.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.noisetype.plains;
2 |
3 | import net.minecraft.util.math.Vec3d;
4 | import net.minecraft.world.gen.ChunkRandom;
5 | import supercoder79.simplexterrain.api.noise.OctaveNoiseSampler;
6 | import supercoder79.simplexterrain.noise.gradient.OpenSimplexNoise;
7 | import supercoder79.simplexterrain.world.BiomeData;
8 | import supercoder79.simplexterrain.world.noisetype.NoiseType;
9 |
10 | import java.util.Map;
11 |
12 | public class LowLyingPlainsNoiseType implements NoiseType {
13 | private OctaveNoiseSampler noise;
14 |
15 | @Override
16 | public void init(ChunkRandom random) {
17 | this.noise = new OctaveNoiseSampler<>(OpenSimplexNoise.class, random, 4, 100, 8, 4);
18 | }
19 |
20 | @Override
21 | public double modify(int x, int z, double currentNoiseValue, double weight, BiomeData data) {
22 | return -1 + this.noise.sample(x, z);
23 | }
24 |
25 | @Override
26 | public void addToPicker(Map points, double theta) {
27 | points.put(new Vec3d(Math.cos(theta) * Math.sqrt(3), 0, Math.sin(theta) * Math.sqrt(3)), this);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/noisetype/plains/MountainsNoiseType.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.noisetype.plains;
2 |
3 | import net.minecraft.util.math.MathHelper;
4 | import net.minecraft.util.registry.RegistryKey;
5 | import net.minecraft.world.biome.Biome;
6 | import net.minecraft.world.biome.BiomeKeys;
7 | import net.minecraft.world.gen.ChunkRandom;
8 | import supercoder79.simplexterrain.api.noise.OctaveNoiseSampler;
9 | import supercoder79.simplexterrain.noise.gradient.OpenSimplexNoise;
10 | import supercoder79.simplexterrain.world.BiomeData;
11 | import supercoder79.simplexterrain.world.noisetype.NoiseType;
12 |
13 | public class MountainsNoiseType implements NoiseType {
14 | private OctaveNoiseSampler noise;
15 | private OctaveNoiseSampler noise2;
16 | private OpenSimplexNoise ridged;
17 |
18 | @Override
19 | public void init(ChunkRandom random) {
20 | this.noise = new OctaveNoiseSampler<>(OpenSimplexNoise.class, random, 4, 160, 40, 20);
21 | this.noise2 = new OctaveNoiseSampler<>(OpenSimplexNoise.class, random, 2, 600, 20, 20);
22 | this.ridged = new OpenSimplexNoise(random.nextLong());
23 | }
24 |
25 | @Override
26 | public double modify(int x, int z, double currentNoiseValue, double weight, BiomeData data) {
27 | double ridgedNoise = Math.abs(1 - this.ridged.sample(x / 120.0, z / 120.0)) * 20 * weight;
28 | double addition = MathHelper.lerp(MathHelper.perlinFade(weight), 0, 20);
29 | double noise = this.noise.sample(x, z) * weight;
30 | noise += this.noise2.sample(x, z) * weight;
31 |
32 | return 3 + addition + noise + ridgedNoise;
33 | }
34 |
35 | @Override
36 | public RegistryKey modifyBiome(int y, RegistryKey existing) {
37 | if (y > 100 - 61) {
38 | return BiomeKeys.TAIGA;
39 | }
40 |
41 | return y > (85 - 61) ? BiomeKeys.FOREST : existing;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/noisetype/plains/PlainsNoiseType.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.noisetype.plains;
2 |
3 | import net.minecraft.world.gen.ChunkRandom;
4 | import supercoder79.simplexterrain.api.noise.OctaveNoiseSampler;
5 | import supercoder79.simplexterrain.noise.gradient.OpenSimplexNoise;
6 | import supercoder79.simplexterrain.world.BiomeData;
7 | import supercoder79.simplexterrain.world.noisetype.NoiseType;
8 |
9 | public class PlainsNoiseType implements NoiseType {
10 | private OctaveNoiseSampler noise;
11 | @Override
12 | public void init(ChunkRandom random) {
13 | this.noise = new OctaveNoiseSampler<>(OpenSimplexNoise.class, random, 4, 140, 12, 12);
14 | }
15 |
16 | @Override
17 | public double modify(int x, int z, double currentNoiseValue, double weight, BiomeData data) {
18 | return 5 + this.noise.sample(x, z);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/postprocessor/ErosionPostProcessor.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.postprocessor;
2 |
3 | import java.nio.file.Paths;
4 | import java.util.Random;
5 |
6 | import net.minecraft.block.Blocks;
7 | import net.minecraft.util.math.BlockPos;
8 | import net.minecraft.util.math.ChunkPos;
9 | import net.minecraft.world.WorldAccess;
10 | import net.minecraft.world.gen.ChunkRandom;
11 | import supercoder79.simplexterrain.SimplexTerrain;
12 | import supercoder79.simplexterrain.api.Heightmap;
13 | import supercoder79.simplexterrain.api.noise.OctaveNoiseSampler;
14 | import supercoder79.simplexterrain.api.postprocess.TerrainPostProcessor;
15 | import supercoder79.simplexterrain.configs.ConfigHelper;
16 | import supercoder79.simplexterrain.configs.postprocessors.ErosionConfigData;
17 | import supercoder79.simplexterrain.noise.gradient.OpenSimplexNoise;
18 |
19 | public class ErosionPostProcessor implements TerrainPostProcessor {
20 | private ErosionConfigData config;
21 | private OctaveNoiseSampler sampler;
22 |
23 | @Override
24 | public void init(long seed) {
25 | sampler = new OctaveNoiseSampler<>(OpenSimplexNoise.class, new ChunkRandom(seed), config.octaves, config.frequency, config.amplitudeHigh, config.amplitudeLow);
26 | }
27 |
28 | @Override
29 | public void setup() {
30 | config = ConfigHelper.getFromConfig(ErosionConfigData.class, Paths.get("config", "simplexterrain", "postprocessors", "erosion.json"));
31 | }
32 |
33 | @Override
34 | public void process(WorldAccess world, Random rand, int chunkX, int chunkZ, Heightmap heightmap) {
35 | int[] heights = heightmap.getHeightsInChunk(new ChunkPos(chunkX, chunkZ));
36 | BlockPos.Mutable mutable = new BlockPos.Mutable();
37 | for (int x = 0; x < 16; x++) {
38 | mutable.setX(chunkX*16 + x);
39 | for (int z = 0; z < 16; z++) {
40 | mutable.setZ(chunkZ*16 + z);
41 | double sample = sampler.sample(chunkX*16 + x, chunkZ*16 + z) + config.baseNoise;
42 | if (sample < config.threshold && heights[x*16 + z] > SimplexTerrain.CONFIG.seaLevel) {
43 | for (int y = 0; y < Math.abs(sample); y++) {
44 | mutable.setY(heights[x*16 + z] - y);
45 | world.setBlockState(mutable, Blocks.AIR.getDefaultState(), 2);
46 | }
47 | if (world.getBlockState(mutable.down()) == Blocks.DIRT.getDefaultState()) world.setBlockState(mutable.down(), Blocks.GRASS_BLOCK.getDefaultState(), 2);
48 |
49 | }
50 | }
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/postprocessor/OldRiverPostProcessor.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.postprocessor;
2 |
3 | import java.nio.file.Paths;
4 | import java.util.Random;
5 |
6 | import net.minecraft.block.BlockState;
7 | import net.minecraft.block.Blocks;
8 | import net.minecraft.util.math.BlockPos;
9 | import net.minecraft.util.math.MathHelper;
10 | import net.minecraft.world.WorldAccess;
11 | import net.minecraft.world.gen.ChunkRandom;
12 | import supercoder79.simplexterrain.api.Coordinate2Function;
13 | import supercoder79.simplexterrain.api.Heightmap;
14 | import supercoder79.simplexterrain.api.noise.OctaveNoiseSampler;
15 | import supercoder79.simplexterrain.api.postprocess.TerrainPostProcessor;
16 | import supercoder79.simplexterrain.configs.ConfigHelper;
17 | import supercoder79.simplexterrain.configs.postprocessors.RiverConfigData;
18 | import supercoder79.simplexterrain.noise.gradient.OpenSimplexNoise;
19 |
20 | public final class OldRiverPostProcessor implements TerrainPostProcessor {
21 | private RiverConfigData config;
22 | private OpenSimplexNoise noiseSampler;
23 | private OctaveNoiseSampler sandNoise;
24 | private ChunkRandom random;
25 |
26 | @Override
27 | public void init(long seed) {
28 | random = new ChunkRandom(seed);
29 | noiseSampler = new OpenSimplexNoise(seed - 8);
30 | sandNoise = new OctaveNoiseSampler<>(OpenSimplexNoise.class, new Random(seed), config.sandNoiseOctaves,config.sandNoiseFrequency, config.sandNoiseAmplitudeHigh, config.sandNoiseAmplitudeLow);
31 | }
32 |
33 | @Override
34 | public void setup() {
35 | config = ConfigHelper.getFromConfig(RiverConfigData.class, Paths.get("config", "simplexterrain", "postprocessors", "rivers.json"));
36 | }
37 |
38 | @Override
39 | public void process(WorldAccess world, Random rand, int chunkX, int chunkZ, Heightmap heightmap) {
40 | BlockPos.Mutable pos = new BlockPos.Mutable();
41 | int startX = (chunkX << 4);
42 | int startZ = (chunkZ << 4);
43 |
44 | for (int localX = 0; localX < 16; ++localX) {
45 | int x = localX + startX;
46 | pos.setX(x);
47 | for (int localZ = 0; localZ < 16; ++localZ) {
48 | int z = localZ + startZ;
49 | pos.setZ(z);
50 | double noise = noiseSampler.sample((double) x / config.scale, (double) z / config.scale);
51 | if (noise > 0.12 && noise < 0.15) {
52 | addRiverInChunk(world, heightmap::getHeight, x, z, pos, 0.13 - noise);
53 | }
54 | }
55 | }
56 | }
57 |
58 | private void addRiverInChunk(WorldAccess world, Coordinate2Function heightFunction, final int x, final int z, BlockPos.Mutable pos, double depthNoise) {
59 | int y = heightFunction.apply(x, z) + 1;
60 | int seaLevel = world.getSeaLevel();
61 |
62 | if (y < seaLevel) {
63 | return;
64 | }
65 |
66 | if (depthNoise < 0) {
67 | depthNoise *= -1;
68 | }
69 | depthNoise = 0.1 - depthNoise;
70 | depthNoise *= (5 * 10);
71 | int depth = (int) depthNoise;
72 |
73 | pos.setY(y);
74 | BlockState bottomBlock = world.getBlockState(pos);
75 |
76 | int waterLevel = calculateWaterLevel(x, z, seaLevel, heightFunction);
77 | int[] neighborWaterLevels = new int[] {
78 | calculateWaterLevel(x+1, z, seaLevel, heightFunction),
79 | calculateWaterLevel(x, z+1, seaLevel, heightFunction),
80 | calculateWaterLevel(x, z-1, seaLevel, heightFunction),
81 | calculateWaterLevel(x-1, z, seaLevel, heightFunction)};
82 | int higher = 0;
83 | for (int i : neighborWaterLevels) {
84 | if (i < waterLevel) {
85 | higher++;
86 | }
87 | }
88 |
89 | if (higher >= 3) waterLevel = neighborWaterLevels[0];
90 |
91 | for (int yOffset = 0; yOffset <= depth; ++yOffset) {
92 | --y;
93 | pos.setY(y);
94 |
95 | if (yOffset == 1) {
96 | bottomBlock = world.getBlockState(pos);
97 | }
98 |
99 | world.setBlockState(pos, y >= waterLevel ? AIR : WATER, 2);
100 |
101 | }
102 |
103 | pos.setY(y - 1);
104 | if (sandNoise.sample(x, z) > 0) bottomBlock = Blocks.SAND.getDefaultState();
105 | world.setBlockState(pos, bottomBlock, 2);
106 |
107 | if (random.nextInt(6) == 0) {
108 | pos.setY(y);
109 | world.setBlockState(pos, random.nextInt(3) > 0 ? Blocks.SEAGRASS.getDefaultState() : Blocks.TALL_SEAGRASS.getDefaultState(), 2);
110 | }
111 | }
112 |
113 | private int calculateWaterLevel(int x, int z, int seaLevel, Coordinate2Function heightFunction) {
114 | int subX = ((x >> 2) << 2);
115 | int subZ = ((z >> 2) << 2);
116 | int subXUpper = subX + 4;
117 | int subZUpper = subZ + 4;
118 |
119 | double xProgress = (double) (x - subX) / 4.0;
120 | double zProgress = (double) (z - subZ) / 4.0;
121 |
122 | double sampleNW = heightFunction.apply(subX, subZ);
123 | double sampleNE = heightFunction.apply(subXUpper, subZ);
124 | double sampleSW = heightFunction.apply(subX, subZUpper);
125 | double sampleSE = heightFunction.apply(subXUpper, subZUpper);
126 |
127 | int returnValue = (int) MathHelper.lerp(zProgress,
128 | MathHelper.lerp(xProgress, sampleNW, sampleNE),
129 | MathHelper.lerp(xProgress, sampleSW, sampleSE));
130 |
131 | return Math.max(returnValue, seaLevel);
132 | }
133 |
134 | public static final BlockState AIR = Blocks.AIR.getDefaultState();
135 | public static final BlockState WATER = Blocks.WATER.getDefaultState();
136 | }
137 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/postprocessor/PostProcessors.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.postprocessor;
2 |
3 | import supercoder79.simplexterrain.api.postprocess.TerrainPostProcessor;
4 |
5 | public enum PostProcessors {
6 | // RIVERS(new OldRiverPostProcessor()),
7 | SIMPLEX_CAVES(new SimplexCavesPostProcessor()),
8 | EROSION(new ErosionPostProcessor()),
9 | STRATA(new StrataPostProcessor()),
10 | SOIL(new SoilPostProcessor());
11 |
12 | public TerrainPostProcessor postProcessor;
13 |
14 | PostProcessors(TerrainPostProcessor postProcessor) {
15 | this.postProcessor = postProcessor;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/postprocessor/SimplexCavesPostProcessor.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.postprocessor;
2 |
3 | import net.minecraft.block.Blocks;
4 | import net.minecraft.util.math.BlockPos;
5 | import net.minecraft.util.math.ChunkPos;
6 | import net.minecraft.world.WorldAccess;
7 | import supercoder79.simplexterrain.api.Heightmap;
8 | import supercoder79.simplexterrain.api.postprocess.PostProcessorTarget;
9 | import supercoder79.simplexterrain.api.postprocess.TerrainPostProcessor;
10 | import supercoder79.simplexterrain.noise.gradient.OpenSimplexNoise;
11 | import supercoder79.simplexterrain.noise.gradient.SimplexStyleNoise;
12 |
13 | import java.util.Arrays;
14 | import java.util.Random;
15 |
16 | public class SimplexCavesPostProcessor implements TerrainPostProcessor {
17 | private SimplexStyleNoise sampler1;
18 | private SimplexStyleNoise sampler2;
19 | private SimplexStyleNoise threshold;
20 | private SimplexStyleNoise threshold2;
21 | private SimplexStyleNoise[] multiEvalInstances;
22 |
23 | @Override
24 | public void init(long seed) {
25 | sampler1 = new SimplexStyleNoise(seed + 79);
26 | sampler2 = new SimplexStyleNoise(seed - 79);
27 | threshold = new SimplexStyleNoise(seed + 89);
28 | threshold2 = new SimplexStyleNoise(seed - 89);
29 | multiEvalInstances = new SimplexStyleNoise[] { sampler1, sampler2, threshold };
30 | }
31 |
32 | @Override
33 | public void setup() {
34 |
35 | }
36 |
37 | @Override
38 | public void process(WorldAccess world, Random rand, int chunkX, int chunkZ, Heightmap heightmap) {
39 | int[] heights = heightmap.getHeightsInChunk(new ChunkPos(chunkX, chunkZ));
40 |
41 | double[] values = new double[multiEvalInstances.length];
42 |
43 | BlockPos.Mutable pos = new BlockPos.Mutable();
44 | for (int x = 0; x < 16; x++) {
45 | pos.setX(chunkX*16 + x);
46 | for (int z = 0; z < 16; z++) {
47 | pos.setZ(chunkZ*16 + z);
48 | int h = heights[x*16 + z]+1;
49 | for (int y = 1; y < h; y++) {
50 |
51 | // Begin to compute threshold
52 | double a = (h - y) - 5.0;
53 | if (a < 0) a = 0; else a *= a;
54 | a += 2.3;
55 | double thresholdBase = (0.03125 / (y + 2.0)) - (0.0234375 / a) + 0.0125;
56 | double thresholdAmp1 = 0.06125 / (y + 7.0);
57 | double thresholdSubAmp2 = 0.04625;
58 |
59 | // Reset values for Y, and calculate noises for this block
60 | Arrays.fill(values, 0);
61 | SimplexStyleNoise.noise3_XZBeforeY(multiEvalInstances, pos.getX() / 70f, y / 50f, pos.getZ() / 70f, values);
62 | double d1 = values[0], d2 = values[1], thresholdNoise1 = values[2];
63 |
64 | // Sum of squared noise values, near zero produces tunnels
65 | double d = (d1 * d1) + (d2 * d2);
66 |
67 | // If the sum of squares is above the maximum value the threshold can take given what we know,
68 | // then we know we don't remove the block here.
69 | double largestPossibleThreshold = thresholdBase + thresholdNoise1 * (thresholdAmp1 + (thresholdNoise1 > 0 ? thresholdSubAmp2 : -thresholdSubAmp2));
70 | if (d > largestPossibleThreshold) continue;
71 |
72 | // If the sum of squares is below the minimum value the threshold can take given what we know,
73 | // then we know we do remove the block here.
74 | double smallestPossibleThreshold = thresholdBase + thresholdNoise1 * (thresholdAmp1 + (thresholdNoise1 > 0 ? -thresholdSubAmp2 : thresholdSubAmp2));
75 | boolean removeBlock = (d < smallestPossibleThreshold);
76 |
77 | // If it's in the range where the second threshold noise makes a difference,
78 | // compute that and then we have our final answer.
79 | if (!removeBlock) {
80 | double thresholdNoise2 = threshold2.sample(pos.getX() / 15f, y / 10f, pos.getZ() / 15f);
81 | double threshold = thresholdBase + thresholdNoise1 * (thresholdAmp1 + thresholdNoise2 * thresholdSubAmp2);
82 | removeBlock = (d < threshold);
83 | }
84 |
85 | if (removeBlock) {
86 | pos.setY(y);
87 | if (world.getBlockState(pos) == Blocks.BEDROCK.getDefaultState()) continue;
88 |
89 | if (y < 11) {
90 | world.setBlockState(pos, Blocks.LAVA.getDefaultState(), 0);
91 | } else {
92 | world.setBlockState(pos, Blocks.AIR.getDefaultState(), 0);
93 | }
94 | }
95 | }
96 | }
97 | }
98 | }
99 |
100 | @Override
101 | public PostProcessorTarget getTarget() {
102 | return PostProcessorTarget.CARVERS;
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/postprocessor/SoilPostProcessor.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.postprocessor;
2 |
3 | import java.nio.file.Paths;
4 | import java.util.Random;
5 |
6 | import net.minecraft.block.Blocks;
7 | import net.minecraft.util.math.BlockPos;
8 | import net.minecraft.util.math.ChunkPos;
9 | import net.minecraft.world.WorldAccess;
10 | import net.minecraft.world.gen.ChunkRandom;
11 | import supercoder79.simplexterrain.api.Heightmap;
12 | import supercoder79.simplexterrain.api.noise.OctaveNoiseSampler;
13 | import supercoder79.simplexterrain.api.postprocess.TerrainPostProcessor;
14 | import supercoder79.simplexterrain.configs.ConfigHelper;
15 | import supercoder79.simplexterrain.configs.postprocessors.SoilConfigData;
16 | import supercoder79.simplexterrain.noise.gradient.OpenSimplexNoise;
17 |
18 | public class SoilPostProcessor implements TerrainPostProcessor {
19 | private SoilConfigData config;
20 | private OctaveNoiseSampler coarseDirtSampler;
21 | private OctaveNoiseSampler podzolSampler;
22 |
23 | @Override
24 | public void init(long seed) {
25 | coarseDirtSampler = new OctaveNoiseSampler<>(OpenSimplexNoise.class, new ChunkRandom(seed + 42), config.coarseDirtOctaves, config.coarseDirtFrequency, config.coarseDirtAmplitudeHigh, config.coarseDirtAmplitudeLow);
26 | podzolSampler = new OctaveNoiseSampler<>(OpenSimplexNoise.class, new ChunkRandom(seed - 42), config.podzolOctaves, config.podzolFrequency, config.podzolAmplitudeHigh, config.podzolAmplitudeLow);
27 | }
28 |
29 | @Override
30 | public void setup() {
31 | config = ConfigHelper.getFromConfig(SoilConfigData.class, Paths.get("config", "simplexterrain", "postprocessors", "soil.json"));
32 | }
33 |
34 | @Override
35 | public void process(WorldAccess world, Random rand, int chunkX, int chunkZ, Heightmap heightmap) {
36 | int[] height = heightmap.getHeightsInChunk(new ChunkPos(chunkX, chunkZ));
37 |
38 | BlockPos.Mutable mutable = new BlockPos.Mutable();
39 | for (int x = 0; x < 16; x++) {
40 | mutable.setX(chunkX*16 + x);
41 | for (int z = 0; z < 16; z++) {
42 | mutable.setZ(chunkZ*16 + z);
43 | mutable.setY(height[x*16 + z]);
44 |
45 | if (world.getBlockState(mutable) == Blocks.GRASS_BLOCK.getDefaultState()) {
46 | if (coarseDirtSampler.sample(mutable.getX(), mutable.getZ()) > config.coarseDirtThreshold)
47 | world.setBlockState(mutable, Blocks.COARSE_DIRT.getDefaultState(), 0);
48 | if (podzolSampler.sample(mutable.getX(), mutable.getZ()) < config.podzolThreshold)
49 | world.setBlockState(mutable, Blocks.PODZOL.getDefaultState(), 0);
50 | }
51 | }
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/supercoder79/simplexterrain/world/postprocessor/StrataPostProcessor.java:
--------------------------------------------------------------------------------
1 | package supercoder79.simplexterrain.world.postprocessor;
2 |
3 | import java.util.Random;
4 |
5 | import net.minecraft.block.BlockState;
6 | import net.minecraft.block.Blocks;
7 | import net.minecraft.util.math.BlockPos;
8 | import net.minecraft.util.math.ChunkPos;
9 | import net.minecraft.world.WorldAccess;
10 | import supercoder79.simplexterrain.api.Heightmap;
11 | import supercoder79.simplexterrain.api.postprocess.TerrainPostProcessor;
12 |
13 | public class StrataPostProcessor implements TerrainPostProcessor {
14 | private static final BlockState[] states = new BlockState[32];
15 |
16 | @Override
17 | public void init(long seed) {
18 | }
19 |
20 | @Override
21 | public void setup() {
22 | for (int i = 0; i < 4; i++) {
23 | states[i*8] = Blocks.STONE.getDefaultState();
24 | states[i*8 + 1] = Blocks.ANDESITE.getDefaultState();
25 | states[i*8 + 2] = Blocks.GRANITE.getDefaultState();
26 | states[i*8 + 3] = Blocks.GRANITE.getDefaultState();
27 | states[i*8 + 4] = Blocks.STONE.getDefaultState();
28 | states[i*8 + 5] = Blocks.DIORITE.getDefaultState();
29 | states[i*8 + 6] = Blocks.ANDESITE.getDefaultState();
30 | states[i*8 + 7] = Blocks.STONE.getDefaultState();
31 | }
32 | }
33 |
34 | @Override
35 | public void process(WorldAccess world, Random rand, int chunkX, int chunkZ, Heightmap heightmap) {
36 | int[] heights = heightmap.getHeightsInChunk(new ChunkPos(chunkX, chunkZ));
37 | BlockPos.Mutable mutable = new BlockPos.Mutable();
38 | for (int x = 0; x < 16; x++) {
39 | mutable.setX(chunkX*16 + x);
40 | for (int z = 0; z < 16; z++) {
41 | mutable.setZ(chunkZ*16 + z);
42 | for (int y = heights[x* 16 + z]; y > 0; y--) {
43 | mutable.setY(y);
44 | if (world.getBlockState(mutable) == Blocks.STONE.getDefaultState()) world.setBlockState(mutable, states[y/8], 2);
45 | }
46 | }
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/resources/assets/simplexterrain/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaskarth/simplexterrain/39d74e99bf060741b40bede9ba3a57ae7fced6b4/src/main/resources/assets/simplexterrain/icon.png
--------------------------------------------------------------------------------
/src/main/resources/assets/simplexterrain/lang/en_us.json:
--------------------------------------------------------------------------------
1 | {
2 | "generator.simplex": "Simplex Terrain"
3 | }
--------------------------------------------------------------------------------
/src/main/resources/fabric.mod.json:
--------------------------------------------------------------------------------
1 | {
2 | "schemaVersion": 1,
3 | "id": "simplexterrain",
4 | "version": "0.7.2",
5 |
6 | "name": "Simplex Terrain",
7 | "description": "Creates worlds with terrain unlike anything else!",
8 | "authors": [
9 | "SuperCoder79",
10 | "Valoeghese"
11 | ],
12 | "contact": {
13 | "homepage": "",
14 | "sources": ""
15 | },
16 |
17 | "license": "LGPLv3",
18 | "icon": "assets/simplexterrain/icon.png",
19 |
20 | "accessWidener" : "simplexterrain.aw",
21 |
22 | "environment": "*",
23 | "entrypoints": {
24 | "main": [
25 | "supercoder79.simplexterrain.SimplexTerrain"
26 | ]
27 | },
28 | "mixins": [
29 | "simplexterrain.mixins.json"
30 | ],
31 |
32 | "depends": {
33 | "fabricloader": ">=0.7.0",
34 | "fabric": "*"
35 | },
36 |
37 | "suggests": {
38 | "winterbiomemod": "*",
39 | "traverse": "*",
40 | "terrestria": "*"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/resources/simplexterrain.aw:
--------------------------------------------------------------------------------
1 | accessWidener v1 named
2 |
3 | accessible method net/minecraft/client/world/GeneratorType (Ljava/lang/String;)V
4 | accessible field net/minecraft/world/biome/source/VanillaLayeredBiomeSource BIOMES Ljava/util/List;
--------------------------------------------------------------------------------
/src/main/resources/simplexterrain.mixins.json:
--------------------------------------------------------------------------------
1 | {
2 | "required": true,
3 | "package": "supercoder79.simplexterrain.mixin",
4 | "compatibilityLevel": "JAVA_8",
5 | "mixins": [
6 | "MixinNoiseChunkGenerator",
7 | "MixinChunkStatus",
8 | "MixinGeneratorOptions",
9 | "MixinMinecraftServer",
10 | "MixinSpawnLocating",
11 | "antibad.MixinLakeFeature",
12 | "optichunk.MixinChunkSection"
13 | ],
14 | "client": [
15 | "client.MixinSkyProperties",
16 | "client.MixinMinecraftClient"
17 | ],
18 | "injectors": {
19 | "defaultRequire": 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------