Skip to content

Instantly share code, notes, and snippets.

@Barteks2x
Last active April 29, 2019 22:08
Show Gist options
  • Save Barteks2x/41e6d542cc32e286bbd938361d49083c to your computer and use it in GitHub Desktop.
Save Barteks2x/41e6d542cc32e286bbd938361d49083c to your computer and use it in GitHub Desktop.
Mixed gradient-value noise that generates it's values based on Minecraft biome data.
package io.github.opencubicchunks.cubicchunks.cubicgen.noise;
import com.flowpowered.noise.Utils;
import com.flowpowered.noise.module.Module;
import io.github.opencubicchunks.cubicchunks.cubicgen.math.Vec2d;
import net.minecraft.util.math.MathHelper;
public class BiomeControlledGradValPerlinNoise extends Module {
private static final int X_NOISE_GEN = 1619;
private static final int Y_NOISE_GEN = 31337;
private static final int Z_NOISE_GEN = 6971;
private static final int SEED_NOISE_GEN = 1013;
private static final int SHIFT_NOISE_GEN = 8;
private final double maxVal;
private int octaves;
private double fx;
private double fy;
private double fz;
private double valleyDepthFactor;
private final double scale;
private final double offset;
private final double fxi;
private final double fyi;
private final double fzi;
private final int seed;
private BiomeDataProvider data;
/**
* Generates a gradient-coherent-noise value from the coordinates of a three-dimensional input value.
*
* @param x The @a x coordinate of the input value.
* @param y The @a y coordinate of the input value.
* @param z The @a z coordinate of the input value.
* @param seed The random number seed.
* @param valleyDepthFactor
* @return The generated gradient-coherent-noise value.
* <p/>
* The return value ranges from 0 to 1.
* <p/>
* For an explanation of the difference between <i>gradient</i> noise and <i>value</i> noise, see the comments for the GradientNoise3D() function.
*/
public static void gradientCoherentNoise3D(double x, double y, double z,
double freqInvX, double freqInvY, double freqInvZ,
int seed, BiomeDataProvider data, double valleyDepthFactor,
Vec2d output) {
// Create a unit-length cube aligned along an integer boundary. This cube
// surrounds the input point.
int x0 = ((x > 0.0) ? (int) x : (int) x - 1);
int x1 = x0 + 1;
int y0 = ((y > 0.0) ? (int) y : (int) y - 1);
int y1 = y0 + 1;
int z0 = ((z > 0.0) ? (int) z : (int) z - 1);
int z1 = z0 + 1;
// world coordinates for biome data
int wx0 = MathHelper.floor(x0 * freqInvX);
int wy0 = MathHelper.floor(y0 * freqInvY);
int wz0 = MathHelper.floor(z0 * freqInvZ);
int wx1 = MathHelper.floor(x1 * freqInvX);
int wy1 = MathHelper.floor(y1 * freqInvY);
int wz1 = MathHelper.floor(z1 * freqInvZ);
// Map the difference between the coordinates of the input value and the
// coordinates of the cube's outer-lower-left vertex onto an S-curve.
double xs = Utils.sCurve5(x - (double) x0);
double ys = Utils.sCurve5(y - (double) y0);
double zs = Utils.sCurve5(z - (double) z0);
// Now calculate the noise values at each vertex of the cube. To generate
// the coherent-noise value at the input point, interpolate these eight
// noise values using the S-curve value as the interpolant (trilinear
// interpolation.)
Vec2d n0 = new Vec2d(0, 0);
Vec2d n1 = new Vec2d(0, 0);
Vec2d ix0 = new Vec2d(0, 0);
Vec2d ix1 = new Vec2d(0, 0);
Vec2d iy0 = new Vec2d(0, 0);
Vec2d iy1 = new Vec2d(0, 0);
gradvalBiomeNoise3D(x, y, z, x0, y0, z0, wx0, wy0, wz0, seed, data, n0);
gradvalBiomeNoise3D(x, y, z, x1, y0, z0, wx1, wy0, wz0, seed, data, n1);
linearInterp(n0, n1, xs, ix0);
gradvalBiomeNoise3D(x, y, z, x0, y1, z0, wx0, wy1, wz0, seed, data, n0);
gradvalBiomeNoise3D(x, y, z, x1, y1, z0, wx1, wy1, wz0, seed, data, n1);
linearInterp(n0, n1, xs, ix1);
linearInterp(ix0, ix1, ys, iy0);
gradvalBiomeNoise3D(x, y, z, x0, y0, z1, wx0, wy0, wz1, seed, data, n0);
gradvalBiomeNoise3D(x, y, z, x1, y0, z1, wx1, wy0, wz1, seed, data, n1);
linearInterp(n0, n1, xs, ix0);
gradvalBiomeNoise3D(x, y, z, x0, y1, z1, wx0, wy1, wz1, seed, data, n0);
gradvalBiomeNoise3D(x, y, z, x1, y1, z1, wx1, wy1, wz1, seed, data, n1);
linearInterp(n0, n1, xs, ix1);
linearInterp(ix0, ix1, ys, iy1);
linearInterp(iy0, iy1, zs, output);
}
private static void linearInterp(Vec2d x0, Vec2d x1, double a, Vec2d out) {
out.x = Utils.linearInterp(x0.x, x1.x, a);
out.y = Utils.linearInterp(x0.y, x1.y, a);
}
/**
* Generates mixed gradient and value noise using biome data basis for given coordinates,
* specified as pair of integer and fractional parts.
* <p>
* The difference between fx and ix must be less than or equal to one.
* The difference between @a fy and @a iy must be less than or equal to one.
* The difference between @a fz and @a iz must be less than or equal to one.
* <p/>
* The noise gen
* <p/>
* The gradient part ranges from -1 to 1 multiplied by biome factor.
* The value part is biome offset.
* <p/>
* This function generates a gradient-noise value by performing the following steps: - It first calculates a random normalized vector based on the nearby integer value passed to this function. -
* It then calculates a new value by adding this vector to the nearby integer value passed to this function. - It then calculates the dot product of the above-generated value and the
* floating-point input value passed to this function.
* <p/>
* A noise function differs from a random-number generator because it always returns the same output value if the same input value is passed to it.
*
* @param fx The floating-point @a x coordinate of the input value.
* @param fy The floating-point @a y coordinate of the input value.
* @param fz The floating-point @a z coordinate of the input value.
* @param ix The integer @a x coordinate of a nearby value.
* @param iy The integer @a y coordinate of a nearby value.
* @param iz The integer @a z coordinate of a nearby value.
* @param wx World x coordinate for biome data
* @param wy World y coordinate for biome data
* @param wz World z coordinate for biome data
* @param seed The random number seed.
* @param data Source of biome data
* @param output The output value. x is gradient component, y is value component
*/
public static void gradvalBiomeNoise3D(double fx, double fy, double fz,
int ix, int iy, int iz,
int wx, int wy, int wz,
int seed, BiomeDataProvider data, Vec2d output) {
data.prepare(wx, wy, wz);
double factor = data.variation() * 2; // * 2 because RANDOM_VECTORS is halved as an optimization
// Randomly generate a gradient vector given the integer coordinates of the
// input value. This implementation generates a random number and uses it
// as an index into a normalized-vector lookup table.
int vectorIndex = (X_NOISE_GEN * ix + Y_NOISE_GEN * iy + Z_NOISE_GEN * iz + SEED_NOISE_GEN * seed);
vectorIndex ^= (vectorIndex >> SHIFT_NOISE_GEN);
vectorIndex &= 0xff;
double xvGradient = Utils.RANDOM_VECTORS[(vectorIndex << 2)] * factor;
double yvGradient = Utils.RANDOM_VECTORS[(vectorIndex << 2) + 1] * factor;
double zvGradient = Utils.RANDOM_VECTORS[(vectorIndex << 2) + 2] * factor;
// Set up us another vector equal to the distance between the two vectors
// passed to this function.
double xvPoint = (fx - ix);
double yvPoint = (fy - iy);
double zvPoint = (fz - iz);
// Now compute the dot product of the gradient vector with the distance
// vector. The resulting value is gradient noise. Apply a scaling and
// offset value so that this noise value ranges from 0 to 1.
output.x = (xvGradient * xvPoint) + (yvGradient * yvPoint) + (zvGradient * zvPoint);
output.y = data.offset();
}
public BiomeControlledGradValPerlinNoise(BiomeDataProvider data, int octaves, long seed,
boolean normalized, double minNorm, double maxNorm,
double fx, double fy, double fz, double valleyDepthFactor) {
super(0);
this.data = data;
this.fx = fx;
this.fy = fy;
this.fz = fz;
fxi = 1.0/fx;
fyi = 1.0/fy;
fzi = 1.0/fz;
maxVal = (Math.pow(0.5, octaves) - 1) / (0.5 - 1);
scale = normalized ? (1.0/maxVal) * (maxNorm - minNorm) / 2 : 1;
offset = normalized ? (maxNorm + minNorm) / 2 : 0;
this.octaves = octaves;
this.valleyDepthFactor = valleyDepthFactor;
this.seed = (int) (seed ^ (seed >>> 32));
}
@Override
public int getSourceModuleCount() {
return 0;
}
@Override
public double getValue(double x, double y, double z) {
double x1 = x;
double y1 = y;
double z1 = z;
double fxi = this.fxi;
double fyi = this.fyi;
double fzi = this.fzi;
double curPersistence = 1.0;
int seed;
x1 *= fx;
y1 *= fy;
z1 *= fz;
Vec2d out = new Vec2d(0, 0);
Vec2d temp = new Vec2d(0, 0);
for (int curOctave = 0; curOctave < octaves; curOctave++) {
// Get the coherent-noise value from the input value and add it to the
// final result.
seed = this.seed + curOctave;
BiomeControlledGradValPerlinNoise.gradientCoherentNoise3D(
x1, y1, z1, fxi, fyi, fzi,
seed, data, valleyDepthFactor, temp);
out.add(temp.mul(curPersistence));
// Prepare the next octave.
x1 *= 2;
y1 *= 2;
z1 *= 2;
fxi *= 0.5;
fyi *= 0.5;
fzi *= 0.5;
curPersistence *= 0.5;
}
out.mul(scale).add(offset);
if (y < out.y) {
out.x *= valleyDepthFactor;
}
return out.x + out.y;
}
public interface BiomeDataProvider {
void prepare(int x, int y, int z);
double variation();
double offset();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment