/** * Copyright (c) 2003-2009, Xith3D Project Group all rights reserved. * * Portions based on the Java3D interface, Copyright by Sun Microsystems. * Many thanks to the developers of Java3D and Sun Microsystems for their * innovation and design. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of the 'Xith3D Project Group' nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) A * RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE */ package org.xith3d.utility.noise; /** * Encapsulates Perlin's method for solid noise generation. * * Adapted from copyrighted source code by Ken Perlin * and F. Kenton Musgrave to accompany: * Texturing and Modeling: A Procedural Approach * Ebert, D., Musgrave, K., Peachey, P., Perlin, K., and Worley, S. * AP Professional, September, 1994. ISBN 0-12-228760-6 * Web site: http://www.cs.umbc.edu/~ebert/book/book.html * * @author Carl Burke */ public class Perlin2 { // *** METHODS OF TERRAIN GENERATION CURRENTLY SUPPORTED public static final int METHOD_BASIC = 1; public static final int METHOD_MULTIFRACTAL = 2; public static final int METHOD_HETERO_TERRAIN = 3; public static final int METHOD_HYBRID_MULTIFRACTAL = 4; public static final int METHOD_RIDGED_MULTIFRACTAL = 5; ///** COLOR INDEX CONSTANTS *** public static final int BLACK = 0; public static final int BLUE0 = 1; public static final int BLUE1 = 9; public static final int LAND0 = 10; public static final int LAND1 = 18; public static final int WHITE = 19; public static int[] rtable = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 32, 48, 64, 80, 96, 112, 128, 255 }; public static int[] gtable = { 0, 0, 16, 32, 48, 64, 80, 96, 112, 128, 255, 240, 224, 208, 192, 176, 160, 144, 128, 255 }; public static int[] btable = { 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 4, 8, 12, 16, 20, 24, 28, 32, 255 }; // *** PRIVATE DATA TO DRIVE TERRAIN CALCULATIONS private int method; private double H; private double lacunarity; private double octaves; private double offset; private double gain; private double[] point; private Noise noise; private double[] vec; /************************************************************ * Methods that use the noise functions to generate height fields * Adapted from code written by F. Kenton Musgrave ************************************************************/ /* * Procedural fBm evaluated at "point"; returns value stored in "value". * * Copyright 1994 F. Kenton Musgrave * * Parameters: * ``H'' is the fractal increment parameter * ``lacunarity'' is the gap between successive frequencies * ``octaves'' is the number of frequencies in the fBm * * 'point' must be a double[2] */ private boolean first_fBm = true; private double[] exponent_array; public boolean latic; // flag for latitude based colour public double land; // percentage of surface covered by land public double water; // percentage of surface covered by water public Perlin2() { point = new double[ 2 ]; method = METHOD_BASIC; H = 0.5; lacunarity = 2.0; octaves = 7.0; } public Perlin2( double hIn, double lacIn, double octIn ) { point = new double[ 2 ]; method = METHOD_BASIC; H = hIn; lacunarity = lacIn; octaves = octIn; noise = new Noise(); } public Perlin2( int methIn, double hIn, double lacIn, double octIn, double offIn, double gainIn, Noise n ) { point = new double[ 2 ]; noise = n; switch ( methIn ) { case METHOD_MULTIFRACTAL: method = METHOD_MULTIFRACTAL; H = hIn; lacunarity = lacIn; octaves = octIn; offset = offIn; break; case METHOD_HETERO_TERRAIN: method = METHOD_HETERO_TERRAIN; H = hIn; lacunarity = lacIn; octaves = octIn; offset = offIn; break; case METHOD_HYBRID_MULTIFRACTAL: method = METHOD_HYBRID_MULTIFRACTAL; H = hIn; lacunarity = lacIn; octaves = octIn; offset = offIn; break; case METHOD_RIDGED_MULTIFRACTAL: method = METHOD_RIDGED_MULTIFRACTAL; H = hIn; lacunarity = lacIn; octaves = octIn; offset = offIn; gain = gainIn; break; default: // don't know which method, so do basic method = METHOD_BASIC; H = hIn; lacunarity = lacIn; octaves = octIn; } } public double gain( double a, double b ) { double p = Math.log( 1.0 - b ) / Math.log( 0.5 ); if ( a < 0.001 ) return ( 0.0 ); if ( a > 0.999 ) return ( 1.0 ); if ( a < 0.5 ) return ( Math.pow( 2.0 * a, p ) / 2.0 ); return ( 1.0 - ( Math.pow( 2.0 * ( 1.0 - a ), p ) / 2.0 ) ); } public double turbulence( double[] v, double freq ) { double t; if ( vec == null ) { vec = new double[ 2 ]; } for ( t = 0.; freq >= 1.; freq /= 2 ) { vec[ 0 ] = freq * v[ 0 ]; vec[ 1 ] = freq * v[ 1 ]; t += ( Math.abs( noise.noise2( vec ) ) / freq ); } return t; } public double fBm( double[] point, double H, double lacunarity, double octaves ) { double value; double frequency; double remainder; int i; /* precompute and store spectral weights */ if ( first_fBm ) { /* seize required memory for exponent_array */ exponent_array = new double[ (int)octaves + 1 ]; frequency = 1.0; for ( i = 0; i <= octaves; i++ ) { /* compute weight for each frequency */ exponent_array[ i ] = Math.pow( frequency, -H ); frequency *= lacunarity; } first_fBm = false; } value = 0.0; /* initialize vars to proper values */ frequency = 1.0; /* inner loop of spectral construction */ for ( i = 0; i < octaves; i++ ) { value += ( noise.noise2( point ) * exponent_array[ i ] ); point[ 0 ] *= lacunarity; point[ 1 ] *= lacunarity; } remainder = octaves - (int)octaves; if ( remainder != 0.0 ) { /* add in ``octaves'' remainder */ /* ``i'' and spatial freq. are preset in loop above */ value += ( remainder * noise.noise2( point ) * exponent_array[ i ] ); } return ( value ); } /* * Procedural multifractal evaluated at "point"; * @return value stored in "value". * * Copyright 1994 F. Kenton Musgrave * * Parameters: * ``H'' determines the highest fractal dimension * ``lacunarity'' is gap between successive frequencies * ``octaves'' is the number of frequencies in the fBm * ``offset'' is the zero offset, which determines multifractality * * Note: this tends to yield very small values, so the results need * to be scaled appropriately. */ public double multifractal( double[] point, double H, double lacunarity, double octaves, double offset ) { double value; double frequency; double remainder; int i; /* precompute and store spectral weights */ if ( first_fBm ) { /* seize required memory for exponent_array */ exponent_array = new double[ (int)octaves + 1 ]; frequency = 1.0; for ( i = 0; i <= octaves; i++ ) { /* compute weight for each frequency */ exponent_array[ i ] = Math.pow( frequency, -H ); frequency *= lacunarity; } first_fBm = false; } value = 1.0; /* initialize vars to proper values */ frequency = 1.0; /* inner loop of multifractal construction */ for ( i = 0; i < octaves; i++ ) { value *= ( offset * frequency * noise.noise2( point ) ); point[ 0 ] *= lacunarity; point[ 1 ] *= lacunarity; } remainder = octaves - (int)octaves; if ( remainder != 0.0 ) { /* add in ``octaves'' remainder */ /* ``i'' and spatial freq. are preset in loop above */ value += ( remainder * noise.noise2( point ) * exponent_array[ i ] ); } return value; } /* * Heterogeneous procedural terrain function: stats by altitude method. * Evaluated at "point"; returns value stored in "value". * * Copyright 1994 F. Kenton Musgrave * * Parameters: * ``H'' determines the fractal dimension of the roughest areas * ``lacunarity'' is the gap between successive frequencies * ``octaves'' is the number of frequencies in the fBm * ``offset'' raises the terrain from `sea level' */ public double Hetero_Terrain( double[] point, double H, double lacunarity, double octaves, double offset ) { double value; double increment; double frequency; double remainder; int i; /* precompute and store spectral weights */ if ( first_fBm ) { /* seize required memory for exponent_array */ exponent_array = new double[ (int)octaves + 1 ]; frequency = 1.0; for ( i = 0; i <= octaves; i++ ) { /* compute weight for each frequency */ exponent_array[ i ] = Math.pow( frequency, -H ); frequency *= lacunarity; } first_fBm = false; } /* first unscaled octave of function; later octaves are scaled */ value = offset + noise.noise2( point ); point[ 0 ] *= lacunarity; point[ 1 ] *= lacunarity; /* spectral construction inner loop, where the fractal is built */ for ( i = 1; i < octaves; i++ ) { /* obtain displaced noise value */ increment = noise.noise2( point ) + offset; /* scale amplitude appropriately for this frequency */ increment *= exponent_array[ i ]; /* scale increment by current `altitude' of function */ increment *= value; /* add increment to ``value'' */ value += increment; /* raise spatial frequency */ point[ 0 ] *= lacunarity; point[ 1 ] *= lacunarity; } /* for */ /* take care of remainder in ``octaves'' */ remainder = octaves - (int)octaves; if ( remainder != 0.0 ) { /* ``i'' and spatial freq. are preset in loop above */ /* note that the main loop code is made shorter here */ /* you may want to that loop more like this */ increment = ( noise.noise2( point ) + offset ) * exponent_array[ i ]; value += ( remainder * increment * value ); } return ( value ); } /* Hybrid additive/multiplicative multifractal terrain model. * * Copyright 1994 F. Kenton Musgrave * * Some good parameter values to start with: * * H: 0.25 * offset: 0.7 */ public double HybridMultifractal( double[] point, double H, double lacunarity, double octaves, double offset ) { double frequency; double result; double signal; double weight; double remainder; int i; /* precompute and store spectral weights */ if ( first_fBm ) { /* seize required memory for exponent_array */ exponent_array = new double[ (int)octaves + 1 ]; frequency = 1.0; for ( i = 0; i <= octaves; i++ ) { /* compute weight for each frequency */ exponent_array[ i ] = Math.pow( frequency, -H ); frequency *= lacunarity; } first_fBm = false; } /* get first octave of function */ result = ( noise.noise2( point ) + offset ) * exponent_array[ 0 ]; weight = result; /* increase frequency */ point[ 0 ] *= lacunarity; point[ 1 ] *= lacunarity; /* spectral construction inner loop, where the fractal is built */ for ( i = 1; i < octaves; i++ ) { /* prevent divergence */ if ( weight > 1.0 ) { weight = 1.0; } /* get next higher frequency */ signal = ( noise.noise2( point ) + offset ) * exponent_array[ i ]; /* add it in, weighted by previous freq's local value */ result += ( weight * signal ); /* update the (monotonically decreasing) weighting value */ /* (this is why H must specify a high fractal dimension) */ weight *= signal; /* increase frequency */ point[ 0 ] *= lacunarity; point[ 1 ] *= lacunarity; } /* for */ /* take care of remainder in ``octaves'' */ remainder = octaves - (int)octaves; if ( remainder != 0.0 ) { /* ``i'' and spatial freq. are preset in loop above */ result += ( remainder * noise.noise2( point ) * exponent_array[ i ] ); } return ( ( result / 2.0 ) - 1.0 ); } /* Ridged multifractal terrain model. * * Copyright 1994 F. Kenton Musgrave * * Some good parameter values to start with: * * H: 1.0 * Lacunarity: 0.5 * offset: 1.0 * gain: 2.0 */ public double RidgedMultifractal( double[] point, double H, double lacunarity, double octaves, double offset, double gain ) { double result; double frequency; double signal; double weight; int i; /* precompute and store spectral weights */ if ( first_fBm ) { /* seize required memory for exponent_array */ exponent_array = new double[ (int)octaves + 1 ]; frequency = 1.0; for ( i = 0; i <= octaves; i++ ) { /* compute weight for each frequency */ exponent_array[ i ] = Math.pow( frequency, -H ); frequency *= lacunarity; } first_fBm = false; } /* get first octave */ signal = noise.noise2( point ); /* get absolute value of signal (this creates the ridges) */ if ( signal < 0.0 ) { signal = -signal; } /* invert and translate (note that "offset" should be ~= 1.0) */ signal = offset - signal; /* square the signal, to increase "sharpness" of ridges */ signal *= signal; /* assign initial values */ result = signal; weight = 1.0; for ( i = 1; i < octaves; i++ ) { /* increase the frequency */ point[ 0 ] *= lacunarity; point[ 1 ] *= lacunarity; /* weight successive contributions by previous signal */ weight = signal * gain; if ( weight > 1.0 ) { weight = 1.0; } if ( weight < 0.0 ) { weight = 0.0; } signal = noise.noise2( point ); if ( signal < 0.0 ) { signal = -signal; } signal = offset - signal; signal *= signal; /* weight the contribution */ signal *= weight; result += ( signal * exponent_array[ i ] ); } return ( ( result - 1.0 ) / 2.0 ); } /** * * @param M * @param W * @param H */ public void setScaling( double M, double W, double H ) { } /** * Calculates an intensity value in [0.0,1.0] at the specified point. */ public double value( double x, double z ) { point[ 0 ] = x; point[ 1 ] = z; switch ( method ) { case METHOD_BASIC: return fBm( point, H, lacunarity, octaves ); case METHOD_MULTIFRACTAL: return multifractal( point, H, lacunarity, octaves, offset ); case METHOD_HETERO_TERRAIN: return Hetero_Terrain( point, H, lacunarity, octaves, offset ); case METHOD_HYBRID_MULTIFRACTAL: return HybridMultifractal( point, H, lacunarity, octaves, offset ); case METHOD_RIDGED_MULTIFRACTAL: return RidgedMultifractal( point, H, lacunarity, octaves, offset, gain ); } return 0.0; } }