/*
* Copyright 2013 MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.terasology.utilities.procedural;
import org.terasology.math.TeraMath;
import org.terasology.utilities.random.FastRandom;
/**
* Improved Perlin noise based on the reference implementation by Ken Perlin.
*
*/
public class PerlinNoise extends AbstractNoise implements Noise2D, Noise3D {
private final int[] noisePermutations;
/**
* Init. a new generator with a given seed value.
*
* @param seed The seed value
*/
public PerlinNoise(long seed) {
FastRandom rand = new FastRandom(seed);
noisePermutations = new int[512];
int[] noiseTable = new int[256];
// Init. the noise table
for (int i = 0; i < 256; i++) {
noiseTable[i] = i;
}
// Shuffle the array
for (int i = 0; i < 256; i++) {
int j = rand.nextInt(256);
int swap = noiseTable[i];
noiseTable[i] = noiseTable[j];
noiseTable[j] = swap;
}
// Finally replicate the noise permutations in the remaining 256 index positions
for (int i = 0; i < 256; i++) {
noisePermutations[i] = noiseTable[i];
noisePermutations[i + 256] = noiseTable[i];
}
}
/**
* Returns the noise value at the given position.
*
* @param posX Position on the x-axis
* @param posY Position on the y-axis
* @param posZ Position on the z-axis
* @return The noise value
*/
@Override
public float noise(float posX, float posY, float posZ) {
int xInt = (int) TeraMath.fastFloor(posX) & 255;
int yInt = (int) TeraMath.fastFloor(posY) & 255;
int zInt = (int) TeraMath.fastFloor(posZ) & 255;
float x = posX - TeraMath.fastFloor(posX);
float y = posY - TeraMath.fastFloor(posY);
float z = posZ - TeraMath.fastFloor(posZ);
float u = TeraMath.fadePerlin(x);
float v = TeraMath.fadePerlin(y);
float w = TeraMath.fadePerlin(z);
int a = noisePermutations[xInt] + yInt;
int aa = noisePermutations[a] + zInt;
int ab = noisePermutations[(a + 1)] + zInt;
int b = noisePermutations[(xInt + 1)] + yInt;
int ba = noisePermutations[b] + zInt;
int bb = noisePermutations[(b + 1)] + zInt;
float gradAA = grad(noisePermutations[aa], x, y, z);
float gradBA = grad(noisePermutations[ba], x - 1, y, z);
float gradAB = grad(noisePermutations[ab], x, y - 1, z);
float gradBB = grad(noisePermutations[bb], x - 1, y - 1, z);
float val1 = TeraMath.lerp(TeraMath.lerp(gradAA, gradBA, u), TeraMath.lerp(gradAB, gradBB, u), v);
float gradAA1 = grad(noisePermutations[(aa + 1)], x, y, z - 1);
float gradBA1 = grad(noisePermutations[(ba + 1)], x - 1, y, z - 1);
float gradAB1 = grad(noisePermutations[(ab + 1)], x, y - 1, z - 1);
float gradBB1 = grad(noisePermutations[(bb + 1)], x - 1, y - 1, z - 1);
float val2 = TeraMath.lerp(TeraMath.lerp(gradAA1, gradBA1, u), TeraMath.lerp(gradAB1, gradBB1, u), v);
return TeraMath.lerp(val1, val2, w);
}
private static float grad(int hash, float x, float y, float z) {
int h = hash & 15;
float u = h < 8 ? x : y;
float v = h < 4 ? y : h == 12 || h == 14 ? x : z;
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
}
}