/* * Copyright 2012 Benjamin Glatzel <benjamin.glatzel@me.com> * * 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; import org.terasology.math.TeraMath; /** * Improved Perlin noise based on the reference implementation by Ken Perlin. * * @author Benjamin Glatzel <benjamin.glatzel@me.com> */ public class PerlinNoise { private static final double LACUNARITY = 2.1379201; private static final double H = 0.836281; private double[] _spectralWeights; private final int[] _noisePermutations; private boolean _recomputeSpectralWeights = true; private int _octaves = 9; /** * Init. a new generator with a given seed value. * * @param seed The seed value */ public PerlinNoise(int 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.randomInt() % 256; j = (j < 0) ? -j : j; 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] = _noisePermutations[i + 256] = _noiseTable[i]; } /** * Returns the noise value at the given position. * * @param x Position on the x-axis * @param y Position on the y-axis * @param z Position on the z-axis * @return The noise value */ public double noise(double x, double y, double z) { int X = (int) TeraMath.fastFloor(x) & 255, Y = (int) TeraMath.fastFloor(y) & 255, Z = (int) TeraMath.fastFloor(z) & 255; x -= TeraMath.fastFloor(x); y -= TeraMath.fastFloor(y); z -= TeraMath.fastFloor(z); double u = fade(x), v = fade(y), w = fade(z); int A = _noisePermutations[X] + Y, AA = _noisePermutations[A] + Z, AB = _noisePermutations[(A + 1)] + Z, B = _noisePermutations[(X + 1)] + Y, BA = _noisePermutations[B] + Z, BB = _noisePermutations[(B + 1)] + Z; return lerp(w, lerp(v, lerp(u, grad(_noisePermutations[AA], x, y, z), grad(_noisePermutations[BA], x - 1, y, z)), lerp(u, grad(_noisePermutations[AB], x, y - 1, z), grad(_noisePermutations[BB], x - 1, y - 1, z))), lerp(v, lerp(u, grad(_noisePermutations[(AA + 1)], x, y, z - 1), grad(_noisePermutations[(BA + 1)], x - 1, y, z - 1)), lerp(u, grad(_noisePermutations[(AB + 1)], x, y - 1, z - 1), grad(_noisePermutations[(BB + 1)], x - 1, y - 1, z - 1)))); } /** * Returns Fractional Brownian Motion at the given position. * * @param x Position on the x-axis * @param y Position on the y-axis * @param z Position on the z-axis * @return The noise value */ public double fBm(double x, double y, double z) { double result = 0.0; if (_recomputeSpectralWeights) { _spectralWeights = new double[_octaves]; for (int i = 0; i < _octaves; i++) _spectralWeights[i] = java.lang.Math.pow(LACUNARITY, -H * i); _recomputeSpectralWeights = false; } for (int i = 0; i < _octaves; i++) { result += noise(x, y, z) * _spectralWeights[i]; x *= LACUNARITY; y *= LACUNARITY; z *= LACUNARITY; } return result; } private static double fade(double t) { return t * t * t * (t * (t * 6 - 15) + 10); } private static double lerp(double t, double a, double b) { return a + t * (b - a); } private static double grad(int hash, double x, double y, double z) { int h = hash & 15; double u = h < 8 ? x : y, v = h < 4 ? y : h == 12 || h == 14 ? x : z; return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); } public void setOctaves(int octaves) { _octaves = octaves; _recomputeSpectralWeights = true; } public int getOctaves() { return _octaves; } }