Files
2024-11-20 15:21:28 +01:00

415 lines
13 KiB
C#

using UnityEngine;
using System.Collections;
namespace Gaia
{
/// <summary>
/// Fractal generator
/// </summary>
public class FractalGenerator
{
/// <summary>
/// The types of fractals we support
/// </summary>
public enum Fractals { Perlin, Billow, RidgeMulti };
/// <summary>
/// Seed - different seeds generate replicable different fractals
/// </summary>
private float m_seed = 0;
public float Seed
{
get { return m_seed; }
set { m_seed = value; }
}
/// <summary>
/// The amount of detail in the fractal - more octaves mean more detail and longer calc time.
/// </summary>
private int m_octaves = 8;
public int Octaves
{
get { return m_octaves; }
set { m_octaves = value; }
}
/// <summary>
/// The roughness of the fractal noise. Controls how quickly amplitudes diminish for successive octaves. 0..1.
/// </summary>
private float m_persistence = 0.65f;
public float Persistence
{
get { return m_persistence; }
set { m_persistence = value; }
}
/// <summary>
/// The frequency of the first octave
/// </summary>
private float m_frequency = 1f;
public float Frequency
{
get { return m_frequency; }
set { m_frequency = value; }
}
/// <summary>
/// The frequency multiplier between successive octaves. Experiment between 1.5 - 3.5.
/// </summary>
private float m_lacunarity = 1.5f;
public float Lacunarity
{
get { return m_lacunarity; }
set { m_lacunarity = value; }
}
/// <summary>
/// Offset X by a given value, allows local fractal area to be explored.
/// </summary>
private float m_XOffset = 0f;
public float XOffset
{
get { return m_XOffset; }
set { m_XOffset = value; }
}
/// <summary>
/// Offset height by a given value. Decease to get islands, increease to get plateaus.
/// </summary>
private float m_ZOffset = 0f;
public float ZOffset
{
get { return m_ZOffset; }
set { m_ZOffset = value; }
}
/// <summary>
/// Offset Y by a given value, allows local fractal area to be explored.
/// </summary>
private float m_YOffset = 0f;
public float YOffset
{
get { return m_YOffset; }
set { m_YOffset = value; }
}
/// <summary>
/// The type of fractal we are generating
/// </summary>
private Fractals m_fractalType = Fractals.Perlin;
public Fractals FractalType
{
get { return m_fractalType; }
set
{
m_fractalType = value;
switch (m_fractalType)
{
case Fractals.Perlin:
{
m_noiseCalculator = GetValue_Perlin;
break;
}
case Fractals.Billow:
{
m_noiseCalculator = GetValue_Billow;
break;
}
case Fractals.RidgeMulti:
{
CalcSpectralWeights();
m_noiseCalculator = GetValue_RidgedMulti;
break;
}
}
}
}
private float[] m_spectralWeights = new float[20]; //Used by ridged fractal
private delegate float GetCalcValue(float x, float z); //Switch in the relevant fractal algorithm
private GetCalcValue m_noiseCalculator; //Switch in the relevant fractal algorithm
/// <summary>
/// Constructor
/// </summary>
public FractalGenerator()
{
FractalType = Fractals.Perlin;
}
/// <summary>
/// Construct and initialise a fractal generator
/// </summary>
/// <param name="frequency">The frequency of the first octave</param>
/// <param name="lacunarity">The frequency multiplier between successive octaves. Experiment between 1.5 - 3.5.</param>
/// <param name="octaves">The amount of detail in the fractal - more octaves mean more detail and longer calc time.</param>
/// <param name="persistance">The roughness of the fractal noise. Controls how quickly amplitudes diminish for successive octaves. 0..1.</param>
/// <param name="seed">Seed - different seeds generate replicable different fractals</param>
/// <param name="type">The type of generator being used</param>
public FractalGenerator(float frequency, float lacunarity, int octaves, float persistance, float seed, Fractals type)
{
m_frequency = frequency;
m_lacunarity = lacunarity;
m_octaves = octaves;
m_persistence = persistance;
m_seed = seed;
switch (type)
{
case Fractals.Perlin:
m_noiseCalculator = GetValue_Perlin;
break;
case Fractals.Billow:
m_noiseCalculator = GetValue_Billow;
break;
case Fractals.RidgeMulti:
CalcSpectralWeights();
m_noiseCalculator = GetValue_RidgedMulti;
break;
default:
m_noiseCalculator = GetValue_Perlin;
break;
}
}
/// <summary>
/// Set some reasonable defaults for the fractal type
/// </summary>
public void SetDefaults()
{
switch (m_fractalType)
{
case Fractals.Perlin:
{
m_frequency = 1f;
m_lacunarity = 2f;
m_octaves = 6;
m_persistence = 0.5f;
m_seed = 0;
m_noiseCalculator = GetValue_Perlin;
break;
}
case Fractals.Billow:
{
m_frequency = 1f;
m_lacunarity = 2f;
m_octaves = 6;
m_persistence = 0.5f;
m_seed = 0;
m_noiseCalculator = GetValue_Billow;
break;
}
case Fractals.RidgeMulti:
{
m_frequency = 1f;
m_lacunarity = 2f;
m_octaves = 6;
m_seed = 0;
CalcSpectralWeights();
m_noiseCalculator = GetValue_RidgedMulti;
break;
}
}
}
/// <summary>
/// Get the noise value at this point
/// </summary>
/// <param name="x">x location</param>
/// <param name="z">z location</param>
/// <returns></returns>
public float GetValue(float x, float z)
{
return m_noiseCalculator(x, z);
}
/// <summary>
/// Get the noise value at this point
/// </summary>
/// <param name="x">x location</param>
/// <param name="z">z location</param>
/// <returns>Value at this point</returns>
public double GetValue(double x, double z)
{
return GetValue((float)x, (float)z);
}
/// <summary>
/// Get the noise value at this point
/// </summary>
/// <param name="x">x location</param>
/// <param name="z">z location</param>
/// <returns>Value in range 0..1 at this point</returns>
public float GetNormalisedValue(float x, float z)
{
return Mathf.Clamp01((GetValue(x, z) + 1f) / 2f);
}
/// <summary>
/// Get the noise value at this point
/// </summary>
/// <param name="x">x location</param>
/// <param name="z">z location</param>
/// <returns>Value in range 0..1 at this point</returns>
public double GetNormalisedValue(double x, double z)
{
return GetNormalisedValue((float)x, (float)z);
}
/// <summary>
/// Calculate a perlin fractal
/// </summary>
/// <param name="x">x location</param>
/// <param name="z">z location</param>
/// <returns></returns>
public float GetValue_Perlin(float x, float z)
{
float value = 0f;
float signal = 0f;
float persistence = 1f;
float nx, nz;
x += m_seed;
z += m_seed;
x += m_XOffset;
z += m_ZOffset;
x *= m_frequency;
z *= m_frequency;
for (int octave = 0; octave < m_octaves; octave++)
{
nx = x;
nz = z;
signal = SimplexNoiseGenerator.Generate(nx, nz);
value += signal * persistence;
x *= m_lacunarity;
z *= m_lacunarity;
persistence *= m_persistence;
}
value += m_YOffset * 2.4f;
return value;
}
/// <summary>
/// Calculate a billow fractal
/// </summary>
/// <param name="x">x location</param>
/// <param name="z">z location</param>
/// <returns></returns>
public float GetValue_Billow(float x, float z)
{
float value = 0f;
float signal = 0f;
float persistence = 1f;
float nx, nz;
x += m_seed;
z += m_seed;
x += m_XOffset;
z += m_ZOffset;
x *= m_frequency;
z *= m_frequency;
for (int octave = 0; octave < m_octaves; octave++)
{
nx = x;
nz = z;
signal = SimplexNoiseGenerator.Generate(nx, nz);
signal = 2f * Mathf.Abs(signal) - 1f;
value += signal * persistence;
x *= m_lacunarity;
z *= m_lacunarity;
persistence *= m_persistence;
}
value += m_YOffset * 2.4f;
return value;
}
/// <summary>
/// Calculate a ridged multi fractal
/// </summary>
/// <param name="x">x location</param>
/// <param name="z">z location</param>
/// <returns></returns>
public float GetValue_RidgedMulti(float x, float z)
{
float signal = 0.0f;
float value = 0.0f;
float weight = 1.0f;
float offset = 1f;
float gain = m_persistence;
float nx, nz;
x += m_seed;
z += m_seed;
x += m_XOffset;
z += m_ZOffset;
x *= m_frequency;
z *= m_frequency;
for (int octave = 0; octave < m_octaves; octave++)
{
nx = x;
nz = z;
//Get the coherent-noise value from input value and add it to final result
signal = SimplexNoiseGenerator.Generate(nx, nz);
//Make the ridges
signal = Mathf.Abs(signal);
signal = offset - signal;
//Square signal to increase sharpness of ridges.
signal *= signal;
//The weighting from previous octave is applied to the signal.
signal *= weight;
//Weight successive contributions by previous signal.
weight = signal * gain;
if (weight > 1.0)
{
weight = 1.0f;
}
if (weight < 0.0f)
{
weight = 0.0f;
}
//Add the signal to output value.
value += signal * m_spectralWeights[octave];
//Next Octave
x *= m_lacunarity;
z *= m_lacunarity;
}
value = (value * 1.25f) - 1.0f;
value += m_YOffset;
return value;
}
/// <summary>
/// Calculate spectral weights for the ridged fractal
/// </summary>
private void CalcSpectralWeights()
{
float h = 1.0f;
float frequency = 1.0f;
int maxSpectra = m_spectralWeights.GetLength(0);
for (int i = 0; i < maxSpectra; i++)
{
m_spectralWeights[i] = Mathf.Pow(frequency, -h);
frequency *= m_lacunarity;
}
}
}
}