001package squidpony.squidmath; 002 003import java.io.Serializable; 004 005/** 006 * Really strange noise functions that typically produce curving black and white shapes when rendered. 007 * This technique uses no floating-point math, surprisingly, which helps its performance a little. 008 * The shapes this produces <a href="https://i.imgur.com/mp23254.png">look like this in 2D</a> and 009 * <a href="https://i.imgur.com/qPLZw0k.gifv">look like this in 3D</a>. MerlinNoise implements 2D and 3D noise 010 * interfaces, allowing it to be used with the various support code in Noise like {@link Noise.Layered2D}. 011 * <br> 012 * MerlinNoise can be a good fit for some kinds of procedural generation that need smoothly-curving patterns that don't 013 * look altogether organic, like paint jobs on a space ship. It does tend to produce a somewhat-noticeable grid. 014 * <br> 015 * This is called Merlin noise because it has a roughly-similar implementation to "classic" Perlin Noise (with hashes 016 * per grid point used to blend values), and because working with noise functions makes me feel like a wizard. 017 * This was a completely unrelated noise algorithm that also avoided floating-point math, but was really pretty awful. 018 */ 019public class MerlinNoise implements Noise.Noise2D, Noise.Noise3D, Serializable { 020 021 private static final long serialVersionUID = 2L; 022 public static final MerlinNoise instance = new MerlinNoise(); 023 public long seed; 024 protected int bits = 8, resolution = 4; 025 private long resSize = 1L << resolution; 026 /** 027 * Constructor for a default MerlinNoise instance with 8-bit output and resolution 3 (yielding 8x8-cell zones that 028 * share their corners). The seed can be set at any point, but it will start at 1. 029 */ 030 public MerlinNoise() { 031 seed = 1L; 032 } 033 034 /** 035 * Constructor for a MerlinNoise instance that allows specification of all parameters. 036 * @param seed the seed to use to alter the generated noise in {@link #noise2D(long, long)} and {@link #noise3D(long, long, long)} 037 * @param bits the number of bits to output; typically 8 to produce byte-sized values 038 * @param resolution an exponent that determines the size of a "zone" of cells that blend between the values at the zone's corners; commonly 1-6 039 */ 040 public MerlinNoise(long seed, int bits, int resolution) 041 { 042 this.seed = seed; 043 this.bits = bits; 044 this.resolution = resolution & 31; 045 resSize = 1L << this.resolution; 046 } 047 048 public long getSeed() { 049 return seed; 050 } 051 052 public void setSeed(long seed) { 053 this.seed = seed; 054 } 055 056 public int getBits() { 057 return bits; 058 } 059 060 /** 061 * Sets the number of bits this will output; 8 is common to produce byte-sized values between 0 and 255. 062 * This value can be between 1 and 64. If bits is 8, then this should produce values of 255 or 0, plus or minus 1. 063 * If bits is some other value, then it may produce more than two values, or only produce one. 064 * @param bits the number of bits of output each call should generate. 065 */ 066 public void setBits(int bits) { 067 this.bits = bits; 068 } 069 070 public int getResolution() { 071 return resolution; 072 } 073 074 /** 075 * Sets the resolution, which is an exponent that determines the width/height of each zone that shares the same four 076 * corners (where only the corners have their own hashed values). If resolution is 1, the size of a zone is 2x2, if 077 * it is 2, then the size of a zone is 4x4, if it is 3, then 8x8, and so on by powers of 2. The resolution can be as 078 * low as 0 (which won't blend corners' hashes at all) or as high as 31, but cannot easily be increased above that 079 * (10 is a really large size for a cell at 1024x1024, and 31 is over 2 billion by 2 billion). This doesn't slow 080 * down significantly (or at all) if resolution is particularly high or low, but this is often between 1 and 6. 081 * @param resolution an int between 0 and 31 082 */ 083 public void setResolution(int resolution) { 084 this.resolution = resolution & 31; 085 resSize = 1L << this.resolution; 086 } 087 088// private static long clorp(long start, long end, long a, long resolution) { 089// a = a * a * ((3L << resolution) - (a << 1)); 090// end = ((1L << resolution * 3) - a) * start + a * end >> resolution * 3; 091// return end; 092// } 093 094 /** 095 * 2D Merlin noise; black and white much of the time but curving instead of angular. 096 * 097 * @param x x input 098 * @param y y input 099 */ 100 public long noise2D(long x, long y) 101 { 102 return noise2D(x, y, seed, resolution, bits); 103 } 104 105 /** 106 * 2D Merlin noise; black and white much of the time but curving instead of angular. 107 * 108 * @param x x input 109 * @param y y input 110 * @param seed the seed to use to alter the generated noise 111 */ 112 public long noise2D(long x, long y, long seed) 113 { 114 return noise2D(x, y, seed, resolution, bits); 115 } 116 117 /** 118 * 3D Merlin noise; black and white much of the time but curving instead of angular. 119 * 120 * @param x x input 121 * @param y y input 122 * @param z z input 123 */ 124 public long noise3D(long x, long y, long z) 125 { 126 return noise3D(x, y, z, seed, resolution, bits); 127 } 128 129 /** 130 * 3D Merlin noise; black and white much of the time but curving instead of angular. 131 * 132 * @param x x input 133 * @param y y input 134 * @param z z input 135 * @param seed the seed to use to alter the generated noise 136 */ 137 public long noise3D(long x, long y, long z, long seed) 138 { 139 return noise3D(x, y, z, seed, resolution, bits); 140 } 141 142 private static long lorp(long start, long end, long a, long resolution) { 143 return ((1L << resolution) - a) * start + a * end >>> resolution; 144 } 145 /** 146 * 2D Merlin noise; black and white much of the time but curving instead of angular. 147 * 148 * @param x x input 149 * @param y y input 150 * @param state state to adjust the output 151 * @param resolution the number of cells between "vertices" where one hashed value is used fully 152 * @param bits how many bits should be used for each (signed long) output; often this is 8 to output a byte 153 * @return noise from {@code -(1L << bits)} to {@code (1L << bits) - 1L}, both inclusive 154 */ 155 public static long noise2D(long x, long y, long state, int resolution, int bits) { 156 long xb = (x >> resolution) + state, yb = (y >> resolution) - state, 157 xr = (x & ~(-1L << resolution)), yr = (y & ~(-1L << resolution)), 158 x0 = LightRNG.determine(xb), x1 = LightRNG.determine(xb + 1), 159 y0 = LightRNG.determine(yb), y1 = LightRNG.determine(yb + 1), 160 x0y0 = (x0 * y0 ^ x0 - y0) >>> resolution, x1y0 = (x1 * y0 ^ x1 - y0) >>> resolution, 161 x0y1 = (x0 * y1 ^ x0 - y1) >>> resolution, x1y1 = (x1 * y1 ^ x1 - y1) >>> resolution; 162 163// x0y0 = Noise.PointHash.hashAll(xb, yb, state) >> resolution, x1y0 = Noise.PointHash.hashAll(xb + 1, yb, state) >> resolution, 164// x0y1 = Noise.PointHash.hashAll(xb, yb + 1, state) >> resolution, x1y1 = Noise.PointHash.hashAll(xb + 1, yb + 1, state) >> resolution; 165 166// x0y0 = (x0y0b >> 2) + (ThrustAltRNG.determine(x0y0b + 1) >> 2) 167// + (ThrustAltRNG.determine(x0y0b + 2) >> 2) + (ThrustAltRNG.determine(x0y0b + 3) >> 2), 168// x1y0 = (x1y0b >> 2) + (ThrustAltRNG.determine(x1y0b + 1) >> 2) 169// + (ThrustAltRNG.determine(x1y0b + 2) >> 2) + (ThrustAltRNG.determine(x1y0b + 3) >> 2), 170// x0y1 = (x0y1b >> 2) + (ThrustAltRNG.determine(x0y1b + 1) >> 2) 171// + (ThrustAltRNG.determine(x0y1b + 2) >> 2) + (ThrustAltRNG.determine(x0y1b + 3) >> 2), 172// x1y1 = (x1y1b >> 2) + (ThrustAltRNG.determine(x1y1b + 1) >> 2) 173// + (ThrustAltRNG.determine(x1y1b + 2) >> 2) + (ThrustAltRNG.determine(x1y1b + 3) >> 2) 174 return lorp(lorp(x0y0, x1y0, xr, resolution), lorp(x0y1, x1y1, xr, resolution), yr, resolution) 175 >>> -resolution - bits; // >> (- bits - resolution & 63) 176 } 177 178 /** 179 * 3D merlin noise. 180 * 181 * @param x x input 182 * @param y y input 183 * @param z z input 184 * @param state state to adjust the output 185 * @param resolution the number of cells between "vertices" where one hashed value is used fully 186 * @param bits how many bits should be used for each (signed long) output; often this is 8 to output a byte 187 * @return noise from {@code -(1L << bits)} to {@code (1L << bits) - 1L}, both inclusive 188 */ 189 public static long noise3D(long x, long y, long z, long state, int resolution, int bits) { 190 long xb = (x >> resolution) + state, yb = (y >> resolution) - state, zb = (z >> resolution) + (0x9E3779B97F4A7C15L ^ state), 191 xr = x & ~(-1L << resolution), yr = y & ~(-1L << resolution), zr = z & ~(-1L << resolution), 192 x0 = LightRNG.determine(xb), x1 = LightRNG.determine(xb + 1), 193 y0 = LightRNG.determine(yb), y1 = LightRNG.determine(yb + 1), 194 z0 = LightRNG.determine(zb), z1 = LightRNG.determine(zb + 1), 195 x0y0z0 = (x0 * y0 * z0 ^ x0 - y0 + (z0 - x0 << 32 | y0 - z0 >>> 32)) >>> resolution, x1y0z0 = (x1 * y0 * z0 ^ x1 - y0 + (z0 - x1 << 32 | y0 - z0 >>> 32)) >>> resolution, 196 x0y1z0 = (x0 * y1 * z0 ^ x0 - y1 + (z0 - x0 << 32 | y1 - z0 >>> 32)) >>> resolution, x1y1z0 = (x1 * y1 * z0 ^ x1 - y1 + (z0 - x1 << 32 | y1 - z0 >>> 32)) >>> resolution, 197 x0y0z1 = (x0 * y0 * z1 ^ x0 - y0 + (z1 - x0 << 32 | y0 - z1 >>> 32)) >>> resolution, x1y0z1 = (x1 * y0 * z1 ^ x1 - y0 + (z1 - x1 << 32 | y0 - z1 >>> 32)) >>> resolution, 198 x0y1z1 = (x0 * y1 * z1 ^ x0 - y1 + (z1 - x0 << 32 | y1 - z1 >>> 32)) >>> resolution, x1y1z1 = (x1 * y1 * z1 ^ x1 - y1 + (z1 - x1 << 32 | y1 - z1 >>> 32)) >>> resolution; 199 200// x0y0z0 = Noise.PointHash.hashAll(xb, yb, zb, state) >> resolution, x1y0z0 = Noise.PointHash.hashAll(xb + 1, yb, zb, state) >> resolution, 201// x0y1z0 = Noise.PointHash.hashAll(xb, yb + 1, zb, state) >> resolution, x1y1z0 = Noise.PointHash.hashAll(xb + 1, yb + 1, zb, state) >> resolution, 202// x0y0z1 = Noise.PointHash.hashAll(xb, yb, zb + 1, state) >> resolution, x1y0z1 = Noise.PointHash.hashAll(xb + 1, yb, zb + 1, state) >> resolution, 203// x0y1z1 = Noise.PointHash.hashAll(xb, yb + 1, zb + 1, state) >> resolution, x1y1z1 = Noise.PointHash.hashAll(xb + 1, yb + 1, zb + 1, state) >> resolution; 204 205// x0y0z0 = (x0y0z0b >> 2) + (ThrustAltRNG.determine(x0y0z0b + 1) >> 2) 206// + (ThrustAltRNG.determine(x0y0z0b + 2) >> 2) + (ThrustAltRNG.determine(x0y0z0b + 3) >> 2), 207// x1y0z0 = (x1y0z0b >> 2) + (ThrustAltRNG.determine(x1y0z0b + 1) >> 2) 208// + (ThrustAltRNG.determine(x1y0z0b + 2) >> 2) + (ThrustAltRNG.determine(x1y0z0b + 3) >> 2), 209// x0y1z0 = (x0y1z0b >> 2) + (ThrustAltRNG.determine(x0y1z0b + 1) >> 2) 210// + (ThrustAltRNG.determine(x0y1z0b + 2) >> 2) + (ThrustAltRNG.determine(x0y1z0b + 3) >> 2), 211// x1y1z0 = (x1y1z0b >> 2) + (ThrustAltRNG.determine(x1y1z0b + 1) >> 2) 212// + (ThrustAltRNG.determine(x1y1z0b + 2) >> 2) + (ThrustAltRNG.determine(x1y1z0b + 3) >> 2), 213// x0y0z1 = (x0y0z1b >> 2) + (ThrustAltRNG.determine(x0y0z1b + 1) >> 2) 214// + (ThrustAltRNG.determine(x0y0z1b + 2) >> 2) + (ThrustAltRNG.determine(x0y0z1b + 3) >> 2), 215// x1y0z1 = (x1y0z1b >> 2) + (ThrustAltRNG.determine(x1y0z1b + 1) >> 2) 216// + (ThrustAltRNG.determine(x1y0z1b + 2) >> 2) + (ThrustAltRNG.determine(x1y0z1b + 3) >> 2), 217// x0y1z1 = (x0y1z1b >> 2) + (ThrustAltRNG.determine(x0y1z1b + 1) >> 2) 218// + (ThrustAltRNG.determine(x0y1z1b + 2) >> 2) + (ThrustAltRNG.determine(x0y1z1b + 3) >> 2), 219// x1y1z1 = (x1y1z1b >> 2) + (ThrustAltRNG.determine(x1y1z1b + 1) >> 2) 220// + (ThrustAltRNG.determine(x1y1z1b + 2) >> 2) + (ThrustAltRNG.determine(x1y1z1b + 3) >> 2); 221// long xm = lorp(x0y0z0, x1y0z0, xr, resolution), 222// xn = lorp(x0y1z0, x1y1z0, xr, resolution), 223// xo = lorp(x0y0z1, x1y0z1, xr, resolution), 224// xp = lorp(x0y1z1, x1y1z1, xr, resolution), 225// ym = lorp(xm, xn, yr, resolution), 226// yn = lorp(xo, xp, yr, resolution), 227// zm = lorp(ym, yn, zr, resolution); 228// zm >>>= -resolution-bits; 229// return zm; 230 return lorp(lorp(lorp(x0y0z0, x1y0z0, xr, resolution), lorp(x0y1z0, x1y1z0, xr, resolution), yr, resolution), 231 lorp(lorp(x0y0z1, x1y0z1, xr, resolution), lorp(x0y1z1, x1y1z1, xr, resolution), yr, resolution), zr, resolution) 232 >>> -resolution - bits; 233 } 234 235 /** 236 * Generates higher-quality continuous-style noise than the other methods, but requires pre-calculating a grid. 237 * Does allow taking a seed because internally it uses a DiverRNG to quickly generate initial white noise before 238 * processing it into more continuous noise. This generates a lot of random numbers (at least 1 + 14 * height, or 239 * more if width is greater than 64), so DiverRNG's high speed and fairly good period are both assets here. 240 * <br> 241 * The 2D int array this produces has ints ranging from 1 to 255, with extreme values very unlikely. Because 0 is 242 * impossible for this to generate, it should be fine to use values from this as denominators in division. 243 * 244 * @param width the width of the 2D int array to generate 245 * @param height the height of the 2D int array to generate 246 * @param seed the RNG seed to use when pseudo-randomly generating the initial white noise this then processes 247 * @return a 2D int array where each int should be between 1 and 255, inclusive 248 */ 249 public static int[][] preCalcNoise2D(int width, int height, long seed) { 250 DiverRNG random = new DiverRNG(seed); 251 int w = (width << 1) + 2, h = (height << 1) + 2; 252 GreasedRegion[] regions = new GreasedRegion[]{ 253 new GreasedRegion(random, w, h).retract().expand(3), new GreasedRegion(random, w, h).retract().expand(3), 254 new GreasedRegion(random, w, h).retract().expand(3), new GreasedRegion(random, w, h).retract().expand(3), 255 new GreasedRegion(random, w, h).retract().expand(3), new GreasedRegion(random, w, h).retract().expand(3), 256 new GreasedRegion(random, w, h).retract().expand(3) 257 }; 258 int[][] data = GreasedRegion.bitSum(regions); 259 260 regions = new GreasedRegion[]{ 261 new GreasedRegion(random, w, h).retract().expand(3), new GreasedRegion(random, w, h).retract().expand(3), 262 new GreasedRegion(random, w, h).retract().expand(3), new GreasedRegion(random, w, h).retract().expand(3), 263 new GreasedRegion(random, w, h).retract().expand(3), new GreasedRegion(random, w, h).retract().expand(3), 264 new GreasedRegion(random, w, h).retract().expand(3) 265 }; 266 int[][] data2 = GreasedRegion.bitSum(regions), data3 = new int[width][height]; 267 for (int x = 0; x < w; x++) { 268 for (int y = 0; y < h; y++) { 269 data[x][y] += 128 - data2[x][y]; 270 } 271 } 272 for (int x = 0, dx = 1; x < width; x++, dx += 2) { 273 for (int y = 0, dy = 1; y < height; y++, dy += 2) { 274 data3[x][y] = ((data[dx][dy] << 2) + data[dx - 1][dy] + data[dx + 1][dy] + data[dx][dy + 1] + data[dx][dy - 1]) >>> 3; 275 } 276 } 277 return data3; 278 /* 279 int[][] data = GreasedRegion.bitSum(regions); 280 return GreasedRegion.selectiveNegate(data, new GreasedRegion(random, width, height), 0xff); 281 */ 282 } 283 284// public static void main(String[] args) 285// { 286// long state = 9999L, bits = 32; 287// for (int resolution = 0; resolution < 4; resolution++) { 288// for (int x = 0; x < 10; x++) { 289// for (int y = 0; y < 10; y++) { 290// long xb = x >>> resolution, yb = y >>> resolution, xr = (x & ~(-1L << resolution)), yr = (y & ~(-1L << resolution)), 291// x0y0 = Noise.PointHash.hashAll(xb, yb, state) >> resolution, x1y0 = Noise.PointHash.hashAll(xb + 1, yb, state) >> resolution, 292// x0y1 = Noise.PointHash.hashAll(xb, yb + 1, state) >> resolution, x1y1 = Noise.PointHash.hashAll(xb + 1, yb + 1, state) >> resolution; 293// long xly0 = lorp(x0y0, x1y0, xr, resolution), xly1 = lorp(x0y1, x1y1, xr, resolution), 294// yl = lorp(xly0, xly1, yr, resolution); 295// System.out.printf("x: %d, y: %d, r: %d = %08X\n", x, y, resolution, yl);// >> (- bits - resolution & 63)); 296// } 297// } 298// } 299// } 300 301 @Override 302 public double getNoise(double x, double y) { 303 return 1 - (noise2D(Noise.longFloor(x * resSize), Noise.longFloor(y * resSize), seed, resolution << 1, 1) << 1); 304 } 305 306 @Override 307 public double getNoiseWithSeed(double x, double y, long seed) { 308 return 1 - (noise2D(Noise.longFloor(x * resSize), Noise.longFloor(y * resSize), seed, resolution << 1, 1) << 1); 309 } 310 311 @Override 312 public double getNoise(double x, double y, double z) { 313 return 1 - (noise3D(Noise.longFloor(x * resSize), Noise.longFloor(y * resSize), Noise.longFloor(z * resSize), seed, resolution << 1, 1) << 1); 314 } 315 316 @Override 317 public double getNoiseWithSeed(double x, double y, double z, long seed) { 318 return 1 - (noise3D(Noise.longFloor(x * resSize), Noise.longFloor(y * resSize), Noise.longFloor(z * resSize), seed, resolution << 1, 1) << 1); 319 } 320}