Add simplex noise utilities (#428)

This commit is contained in:
RecursivePineapple authored and GitHub committed 2026-09-07 06:31:33 -04:00
1 parent e072b8aae7
commit 766b8f483c
9 files changed
+765

No files matched your search

+16
View File
@@ -1,6 +1,7 @@
plugins {
id("com.gtnewhorizons.gtnhconvention")
id("me.champeau.jmh") version "0.7.3"
}
minecraft {
@@ -80,3 +81,18 @@ for (jarTask in listOf(tasks.jar, tasks.shadowJar, tasks.sourcesJar)) {
}
}
}
sourceSets {
named("jmh") {
compileClasspath += sourceSets["patchedMc"].output
runtimeClasspath = files(main17.output) + runtimeClasspath + files(sourceSets["patchedMc"].output)
}
}
dependencies {
add("jmhRuntimeOnly", files(sourceSets["patchedMc"].output))
}
tasks.named<me.champeau.jmh.JMHTask>("jmh") {
dependsOn(main17.compileJavaTaskName)
}
@@ -0,0 +1,72 @@
package com.gtnewhorizon.gtnhlib.noise;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(1)
public class OctavesSamplerBenchmark {
static final int GRID = 256;
@Param({ "4", "8" })
public int octaveCount;
private OctavesSampler sampler;
private int counter;
private double[] xs, ys, zs, out;
@Setup
public void setup() {
sampler = new OctavesSampler(new Random(42), octaveCount);
counter = 0;
xs = new double[GRID];
ys = new double[GRID];
zs = new double[GRID];
out = new double[GRID];
for (int i = 0; i < GRID; i++) {
xs[i] = (i & 15) * 0.5;
ys[i] = (i >> 4) * 0.5;
zs[i] = (i & 31) * 0.5;
}
}
@Benchmark
public void sample2D(Blackhole bh) {
bh.consume(sampler.sample(1.5, 2.7));
}
@Benchmark
public void sample3D(Blackhole bh) {
bh.consume(sampler.sample(1.5, 2.7, 3.9));
}
@Benchmark
public void sample2DGrid(Blackhole bh) {
int c = counter++;
bh.consume(sampler.sample((c & 255) * 0.5, ((c >> 8) & 255) * 0.5));
}
@Benchmark
public void sample3DGrid(Blackhole bh) {
int c = counter++;
bh.consume(sampler.sample((c & 63) * 0.5, ((c >> 6) & 63) * 0.5, ((c >> 12) & 63) * 0.5));
}
}
@@ -0,0 +1,56 @@
package com.gtnewhorizon.gtnhlib.noise;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(1)
public class SimplexSamplerBenchmark {
private SimplexSampler sampler;
private int counter;
@Setup
public void setup() {
sampler = new SimplexSampler(new Random(42));
counter = 0;
}
@Benchmark
public void sample2D(Blackhole bh) {
bh.consume(sampler.sample(1.5, 2.7));
}
@Benchmark
public void sample3D(Blackhole bh) {
bh.consume(sampler.sample(1.5, 2.7, 3.9));
}
@Benchmark
public void sample2DGrid(Blackhole bh) {
int c = counter++;
bh.consume(sampler.sample((c & 255) * 0.5, ((c >> 8) & 255) * 0.5));
}
@Benchmark
public void sample3DGrid(Blackhole bh) {
int c = counter++;
bh.consume(sampler.sample((c & 63) * 0.5, ((c >> 6) & 63) * 0.5, ((c >> 12) & 63) * 0.5));
}
}
@@ -0,0 +1,16 @@
package com.gtnewhorizon.gtnhlib.noise;
/// A generic noise sampler, typically used for worldgen.
/// The domain of the result MUST BE -1 to 1, but the distribution curve is undefined.
/// The coordinates can be any non-NaN/non-infinity value, positive or negative.
/// [SimplexSampler] will produce a 'boxy' standard distribution.
/// Use a [NormalizedSampler] to convert it into an approximately linear distribution.
/// Use a [ScaledSampler] to automatically multiply the passed-in coordinates.
/// Use an [OctavesSampler] to layer several simplex samplers on top of each other, to increase the complexity and
/// detail of the generated noise.
public interface NoiseSampler {
double sample(double x, double y);
double sample(double x, double y, double z);
}
@@ -0,0 +1,53 @@
package com.gtnewhorizon.gtnhlib.noise;
/// A sampler that remaps the 'boxy' normal distribution of a [SimplexSampler] to an approximately linear distribution.
public class NormalizedSampler implements NoiseSampler {
private static final double REMAP_EXPONENT = 0.65;
private static final int LUT_SIZE = 1024;
private static final float[] REMAP_LOOKUP_TABLE;
static {
REMAP_LOOKUP_TABLE = buildLut(REMAP_EXPONENT);
}
private static double clamp(double val, double lo, double hi) {
return val < lo ? lo : val > hi ? hi : val;
}
private static float remap(double v) {
v = clamp(v, -1.0, 1.0);
double t = (v + 1.0) * 0.5 * (LUT_SIZE - 1);
int lo = (int) t;
int hi = Math.min(lo + 1, LUT_SIZE - 1);
float frac = (float) (t - lo);
return REMAP_LOOKUP_TABLE[lo] + frac * (REMAP_LOOKUP_TABLE[hi] - REMAP_LOOKUP_TABLE[lo]);
}
private static float[] buildLut(double exponent) {
float[] lut = new float[LUT_SIZE];
for (int i = 0; i < LUT_SIZE; i++) {
double v = i / (double) (LUT_SIZE - 1) * 2.0 - 1.0; // map i → [-1, 1]
lut[i] = (float) (Math.signum(v) * Math.pow(Math.abs(v), exponent));
}
return lut;
}
private final NoiseSampler base;
public NormalizedSampler(NoiseSampler base) {
this.base = base;
}
@Override
public double sample(double x, double y) {
return remap(base.sample(x, y));
}
@Override
public double sample(double x, double y, double z) {
return remap(base.sample(x, y, z));
}
}
@@ -0,0 +1,96 @@
package com.gtnewhorizon.gtnhlib.noise;
import java.util.Random;
import java.util.function.Supplier;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/// Layers several samplers on top of each other.
/// More octaves increase the CPU cost linearly, but increase the complexity and detail of the returned noise.
/// Each octave has an increasing scale (smaller features) and a decreasing amplitude (smaller effect).
public class OctavesSampler implements NoiseSampler {
private final @NotNull NoiseSampler @NotNull [] octaves;
private final double[] normalizedAmplitudes, scales;
public OctavesSampler(@Nullable NoiseSampler @NotNull [] samplers, double[] amplitudes, double[] scales) {
int samplerCount = 0;
for (NoiseSampler s : samplers) {
if (s != null) samplerCount++;
}
this.octaves = new NoiseSampler[samplerCount];
this.normalizedAmplitudes = new double[samplerCount];
this.scales = new double[samplerCount];
double sum = 0;
for (double amp : amplitudes) {
sum += amp;
}
double norm = 1d / sum;
int samplerCursor = 0;
for (int i = 0; i < samplers.length; i++) {
if (samplers[i] != null) {
this.octaves[samplerCursor] = samplers[i];
this.normalizedAmplitudes[samplerCursor] = amplitudes[i] * norm;
this.scales[samplerCursor] = scales[i];
samplerCursor++;
}
}
}
public OctavesSampler(Supplier<NoiseSampler> samplers, int octaves) {
this.octaves = new NoiseSampler[octaves];
this.normalizedAmplitudes = new double[octaves];
this.scales = new double[octaves];
double sum = 0;
for (int i = 0; i < octaves; i++) {
this.octaves[i] = samplers.get();
double amp = 1d / Math.pow(2d, i);
this.normalizedAmplitudes[i] = amp;
this.scales[i] = Math.pow(2d, i);
sum += amp;
}
double norm = 1d / sum;
for (int i = 0; i < octaves; i++) {
this.normalizedAmplitudes[i] *= norm;
}
}
public OctavesSampler(Random rng, int octaves) {
this(() -> new SimplexSampler(rng), octaves);
}
@Override
public double sample(double x, double y) {
double value = 0;
for (int i = 0, len = octaves.length; i < len; i++) {
double scale = scales[i];
value += octaves[i].sample(x * scale, y * scale) * normalizedAmplitudes[i];
}
return value;
}
@Override
public double sample(double x, double y, double z) {
double value = 0;
for (int i = 0, len = octaves.length; i < len; i++) {
double scale = scales[i];
value += octaves[i].sample(x * scale, y * scale, z * scale) * normalizedAmplitudes[i];
}
return value;
}
}
@@ -0,0 +1,248 @@
/*****************************************************************************
* J3D.org Copyright (c) 2000 Java Source This source is licensed under the GNU LGPL v2.1 Please read
* http://www.gnu.org/copyleft/lgpl.html for more information This software comes with the standard NO WARRANTY
* disclaimer for any purpose. Use it at your own risk. If there's a problem you get to fix it.
*
****************************************************************************/
package com.gtnewhorizon.gtnhlib.noise;
import java.util.Random;
/// A 2d or 3d perlin sampler.
/// This is largely copied from
/// [RWG](https://github.com/GTNewHorizons/Realistic-World-Gen/blob/37046ef9a2f807ae4feb0ad87e7ae65f756839a6/src/main/java/rwg/util/PerlinNoise.java),
/// with the irrelevant code stripped out.
public class PerlinSampler implements NoiseSampler {
// Constants for setting up the Perlin-1 noise functions
private static final int B = 0x1000;
private static final int BM = 0xff;
private static final int N = 0x1000;
/** Default sample size to work with */
private static final int DEFAULT_SAMPLE_SIZE = 256;
/** Permutation array for the improved noise function */
private final int[] p_imp;
/** P array for perline 1 noise */
private final int[] p = new int[B + B + 2];
private final float[] g3 = new float[(B + B + 2) * 3];
private final float[] g2 = new float[(B + B + 2) * 2];
/**
* Create a new noise creator with the default seed value
*/
public PerlinSampler() {
this(100);
}
/**
* Create a new noise creator with the given seed value for the randomness
*
* @param seed The seed value to use
*/
public PerlinSampler(long seed) {
p_imp = new int[DEFAULT_SAMPLE_SIZE << 1];
int i, j, k;
Random rand = new Random(seed);
// Calculate the table of pseudo-random coefficients.
for (i = 0; i < DEFAULT_SAMPLE_SIZE; i++) p_imp[i] = i;
// generate the pseudo-random permutation table.
while (--i > 0) {
k = p_imp[i];
j = (int) (rand.nextLong() & DEFAULT_SAMPLE_SIZE);
p_imp[i] = p_imp[j];
p_imp[j] = k;
}
initPerlin1(rand);
}
/**
* Computes noise function for three dimensions at the point (x,y,z).
*
* @param x x dimension parameter
* @param y y dimension parameter
* @param z z dimension parameter
* @return the noise value at the point (x, y, z)
*/
@Override
public double sample(double x, double y, double z) {
// Constraint the point to a unit cube
double fx = Math.floor(x);
double fy = Math.floor(y);
double fz = Math.floor(z);
int uc_x = (int) fx & 255;
int uc_y = (int) fy & 255;
int uc_z = (int) fz & 255;
// Relative location of the point in the unit cube
double xo = x - fx;
double yo = y - fy;
double zo = z - fz;
// Fade curves for x, y and z
double u = fade(xo);
double v = fade(yo);
double w = fade(zo);
// Generate a hash for each coordinate to find out where in the cube
// it lies.
int a = p_imp[uc_x] + uc_y;
int aa = p_imp[a] + uc_z;
int ab = p_imp[a + 1] + uc_z;
int b = p_imp[uc_x + 1] + uc_y;
int ba = p_imp[b] + uc_z;
int bb = p_imp[b + 1] + uc_z;
// blend results from the 8 corners based on the noise function
double c1 = grad(p_imp[aa], xo, yo, zo);
double c2 = grad(p_imp[ba], xo - 1, yo, zo);
double c3 = grad(p_imp[ab], xo, yo - 1, zo);
double c4 = grad(p_imp[bb], xo - 1, yo - 1, zo);
double c5 = grad(p_imp[aa + 1], xo, yo, zo - 1);
double c6 = grad(p_imp[ba + 1], xo - 1, yo, zo - 1);
double c7 = grad(p_imp[ab + 1], xo, yo - 1, zo - 1);
double c8 = grad(p_imp[bb + 1], xo - 1, yo - 1, zo - 1);
return lerp(w, lerp(v, lerp(u, c1, c2), lerp(u, c3, c4)), lerp(v, lerp(u, c5, c6), lerp(u, c7, c8)));
}
/**
* Create noise in a 2D space using the original Perlin noise algorithm.
*
* @param x The X coordinate of the location to sample
* @param y The Y coordinate of the location to sample
* @return A noisy value at the given position
*/
@Override
public double sample(double x, double y) {
double t = x + N;
t = Math.abs(t);
int bx0 = (int) t & BM;
int bx1 = bx0 + 1 & BM;
double rx0 = t - (int) t;
double rx1 = rx0 - 1;
t = y + N;
t = Math.abs(t);
int by0 = (int) t & BM;
int by1 = by0 + 1 & BM;
double ry0 = t - (int) t;
double ry1 = ry0 - 1;
int i = p[bx0];
int j = p[bx1];
int b00 = p[i + by0];
int b10 = p[j + by0];
int b01 = p[i + by1];
int b11 = p[j + by1];
double sx = sCurve(rx0);
double sy = sCurve(ry0);
double u = rx0 * g2[b00 * 2] + ry0 * g2[b00 * 2 + 1];
double v = rx1 * g2[b10 * 2] + ry0 * g2[b10 * 2 + 1];
double a = lerp(sx, u, v);
u = rx0 * g2[b01 * 2] + ry1 * g2[b01 * 2 + 1];
v = rx1 * g2[b11 * 2] + ry1 * g2[b11 * 2 + 1];
double b = lerp(sx, u, v);
return lerp(sy, a, b);
}
/**
* Simple lerp function using doubles.
*/
private double lerp(double t, double a, double b) {
return a + t * (b - a);
}
/**
* Fade curve calculation which is 6t^5 - 15t^4 + 10t^3. This is the new algorithm, where the old one used to be
* 3t^2 - 2t^3.
*
* @param t The t parameter to calculate the fade for
* @return the drop-off amount.
*/
private double fade(double t) {
return t * t * t * (t * (t * 6 - 15) + 10);
}
/**
* Calculate the gradient function based on the hash code.
*/
private double grad(int hash, double x, double y, double z) {
// Convert low 4 bits of hash code into 12 gradient directions.
int h = hash & 15;
double u = h < 8 || h == 12 || h == 13 ? x : y;
double v = h < 4 || h == 12 || h == 13 ? y : z;
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
}
/**
* S-curve function for value distribution for Perlin-1 noise function.
*/
private double sCurve(double t) {
return t * t * (3 - 2 * t);
}
/**
* 2D-vector normalization function.
*/
private void normalize2(float[] v, int off) {
float s = (float) (1 / Math.sqrt(v[off] * v[off] + v[off + 1] * v[off + 1]));
v[off] *= s;
v[off + 1] *= s;
}
/**
* 3D-vector normalization function.
*/
private void normalize3(float[] v, int off) {
float s = (float) (1 / Math.sqrt(v[off] * v[off] + v[off + 1] * v[off + 1] + v[off + 2] * v[off + 2]));
v[off] *= s;
v[off + 1] *= s;
v[off + 2] *= s;
}
/**
* Initialize the lookup arrays used by Perlin 1 function.
*/
private void initPerlin1(Random rand) {
int i, j, k;
for (i = 0; i < B; i++) {
p[i] = i;
for (j = 0; j < 2; j++) g2[i * 2 + j] = (float) (rand.nextDouble() * Integer.MAX_VALUE % (B + B) - B) / B;
normalize2(g2, i * 2);
for (j = 0; j < 3; j++) g3[i * 3 + j] = (float) (rand.nextDouble() * Integer.MAX_VALUE % (B + B) - B) / B;
normalize3(g3, i * 3);
}
while (--i > 0) {
k = p[i];
j = (int) (rand.nextDouble() * Integer.MAX_VALUE % B);
p[i] = p[j];
p[j] = k;
}
for (i = 0; i < B + 2; i++) {
p[B + i] = p[i];
for (j = 0; j < 2; j++) g2[(B + i) * 2 + j] = g2[i * 2 + j];
for (j = 0; j < 3; j++) g3[(B + i) * 3 + j] = g3[i * 3 + j];
}
}
}
@@ -0,0 +1,34 @@
package com.gtnewhorizon.gtnhlib.noise;
/// Scales another sampler by a certain amount in each axis.
/// Effects are the opposite of what you'd expect - scaling by 2 in an axis shrinks the noise's feature size by half
/// along that axis.
/// This does not affect the noise's output domain.
public class ScaledSampler implements NoiseSampler {
private final NoiseSampler base;
private final double scaleX;
private final double scaleY;
private final double scaleZ;
public ScaledSampler(NoiseSampler base, double scaleX, double scaleY, double scaleZ) {
this.base = base;
this.scaleX = scaleX;
this.scaleY = scaleY;
this.scaleZ = scaleZ;
}
public ScaledSampler(NoiseSampler base, double scale) {
this(base, scale, scale, scale);
}
@Override
public double sample(double x, double y) {
return base.sample(x * scaleX, y * scaleY);
}
@Override
public double sample(double x, double y, double z) {
return base.sample(x * scaleX, y * scaleY, z * scaleZ);
}
}
@@ -0,0 +1,174 @@
package com.gtnewhorizon.gtnhlib.noise;
import java.util.Random;
import net.minecraft.util.MathHelper;
/// A standard simplex noise sampler.
public class SimplexSampler implements NoiseSampler {
protected static final int[][] GRADIENTS = new int[][] { { 1, 1, 0 }, { -1, 1, 0 }, { 1, -1, 0 }, { -1, -1, 0 },
{ 1, 0, 1 }, { -1, 0, 1 }, { 1, 0, -1 }, { -1, 0, -1 }, { 0, 1, 1 }, { 0, -1, 1 }, { 0, 1, -1 },
{ 0, -1, -1 }, { 1, 1, 0 }, { 0, -1, 1 }, { -1, 1, 0 }, { 0, -1, -1 }, };
private static final double SQRT_3 = Math.sqrt(3.0D);
private static final double SKEW_FACTOR_2D = 0.5D * (SQRT_3 - 1.0D);
private static final double UNSKEW_FACTOR_2D = (3.0D - SQRT_3) / 6.0D;
private final int[] permutations = new int[512];
public final double originX;
public final double originY;
public final double originZ;
public SimplexSampler(Random random) {
this.originX = random.nextDouble() * 256.0D;
this.originY = random.nextDouble() * 256.0D;
this.originZ = random.nextDouble() * 256.0D;
for (int i = 0; i < 256; i++) {
this.permutations[i] = i;
}
for (int i = 0; i < 256; ++i) {
int k = random.nextInt(256 - i);
int l = this.permutations[i];
this.permutations[i] = this.permutations[k + i];
this.permutations[k + i] = l;
}
}
private int getGradient(int hash) {
return this.permutations[hash & 255];
}
protected static double dot(int[] gArr, double x, double y, double z) {
return (double) gArr[0] * x + (double) gArr[1] * y + (double) gArr[2] * z;
}
private double grad(int hash, double x, double y, double z, double distance) {
double d = distance - x * x - y * y - z * z;
double f;
if (d < 0.0D) {
f = 0.0D;
} else {
d *= d;
f = d * d * dot(GRADIENTS[hash], x, y, z);
}
return f;
}
@Override
public double sample(double x, double y) {
double d = (x + y) * SKEW_FACTOR_2D;
int i = MathHelper.floor_double(x + d);
int j = MathHelper.floor_double(y + d);
double e = (double) (i + j) * UNSKEW_FACTOR_2D;
double f = (double) i - e;
double g = (double) j - e;
double h = x - f;
double k = y - g;
byte n = (byte) (h > k ? 1 : 0);
byte o = (byte) (h > k ? 0 : 1);
double p = h - (double) n + UNSKEW_FACTOR_2D;
double q = k - (double) o + UNSKEW_FACTOR_2D;
double r = h - 1.0D + 2.0D * UNSKEW_FACTOR_2D;
double s = k - 1.0D + 2.0D * UNSKEW_FACTOR_2D;
int t = i & 255;
int u = j & 255;
int v = this.getGradient(t + this.getGradient(u)) % 12;
int w = this.getGradient(t + n + this.getGradient(u + o)) % 12;
int z = this.getGradient(t + 1 + this.getGradient(u + 1)) % 12;
double aa = this.grad(v, h, k, 0.0D, 0.5D);
double ab = this.grad(w, p, q, 0.0D, 0.5D);
double ac = this.grad(z, r, s, 0.0D, 0.5D);
return 70.0D * (aa + ab + ac);
}
@Override
public double sample(double x, double y, double z) {
double e = (x + y + z) * 0.3333333333333333D;
int i = MathHelper.floor_double(x + e);
int j = MathHelper.floor_double(y + e);
int k = MathHelper.floor_double(z + e);
double g = (double) (i + j + k) * 0.16666666666666666D;
double h = (double) i - g;
double l = (double) j - g;
double m = (double) k - g;
double n = x - h;
double o = y - l;
double p = z - m;
byte w;
byte aa;
byte ab;
byte ac;
byte ad;
byte bc;
if (n >= o) {
if (o >= p) {
w = 1;
aa = 0;
ab = 0;
ac = 1;
ad = 1;
bc = 0;
} else if (n >= p) {
w = 1;
aa = 0;
ab = 0;
ac = 1;
ad = 0;
bc = 1;
} else {
w = 0;
aa = 0;
ab = 1;
ac = 1;
ad = 0;
bc = 1;
}
} else if (o < p) {
w = 0;
aa = 0;
ab = 1;
ac = 0;
ad = 1;
bc = 1;
} else if (n < p) {
w = 0;
aa = 1;
ab = 0;
ac = 0;
ad = 1;
bc = 1;
} else {
w = 0;
aa = 1;
ab = 0;
ac = 1;
ad = 1;
bc = 0;
}
double bd = n - (double) w + 0.16666666666666666D;
double be = o - (double) aa + 0.16666666666666666D;
double bf = p - (double) ab + 0.16666666666666666D;
double bg = n - (double) ac + 0.3333333333333333D;
double bh = o - (double) ad + 0.3333333333333333D;
double bi = p - (double) bc + 0.3333333333333333D;
double bj = n - 1.0D + 0.5D;
double bk = o - 1.0D + 0.5D;
double bl = p - 1.0D + 0.5D;
int bm = i & 255;
int bn = j & 255;
int bo = k & 255;
int bp = this.getGradient(bm + this.getGradient(bn + this.getGradient(bo))) % 12;
int bq = this.getGradient(bm + w + this.getGradient(bn + aa + this.getGradient(bo + ab))) % 12;
int br = this.getGradient(bm + ac + this.getGradient(bn + ad + this.getGradient(bo + bc))) % 12;
int bs = this.getGradient(bm + 1 + this.getGradient(bn + 1 + this.getGradient(bo + 1))) % 12;
double bt = this.grad(bp, n, o, p, 0.6D);
double bu = this.grad(bq, bd, be, bf, 0.6D);
double bv = this.grad(br, bg, bh, bi, 0.6D);
double bw = this.grad(bs, bj, bk, bl, 0.6D);
return 32.0D * (bt + bu + bv + bw);
}
}