namespace Sandbox.Utility;
#nullable enable
///
/// A noise function that can be sampled at a 1-, 2-, or 3D position.
/// Samples will be between 0 and 1. Thread-safe.
///
public interface INoiseField
{
///
/// Sample at a 1D position.
///
/// A noise value between 0 and 1.
[Pure] public float Sample( float x ) => Sample( x, 0f );
///
/// Sample at a 2D position.
///
/// A noise value between 0 and 1.
[Pure] public float Sample( float x, float y ) => Sample( x, y, 0f );
///
/// Sample at a 3D position.
///
/// A noise value between 0 and 1.
[Pure] float Sample( float x, float y, float z );
///
/// Sample at a 2D position.
///
/// A noise value between 0 and 1.
[Pure] public float Sample( Vector2 vec ) => Sample( vec.x, vec.y );
///
/// Sample at a 3D position.
///
/// A noise value between 0 and 1.
[Pure] public float Sample( Vector3 vec ) => Sample( vec.x, vec.y, vec.z );
}
partial class Noise
{
///
/// Parameters for constructing a noise field. Use if you
/// want a noise field made from multiple octaves.
///
/// Seed state to initialize the field with.
/// How quickly should samples change across space.
public record Parameters(
int Seed = 5633,
float Frequency = 0.01f );
///
/// Parameters for constructing a fractal
/// noise field, which layers multiple octaves of a noise function with increasing frequency
/// and reducing amplitudes.
///
/// Seed state to initialize the field with.
/// How quickly should samples change across space.
/// How many layers of noise to use.
/// How much to multiply the amplitude of each successive octave by.
/// How much to multiply the frequency of each successive octave by.
public record FractalParameters(
int Seed = 5633,
float Frequency = 0.01f,
int Octaves = 4,
float Gain = 0.5f,
float Lacunarity = 2f )
: Parameters( Seed, Frequency );
///
/// Creates a Value noise field,
/// effectively smoothly sampled white noise. Use a for the
/// field to have multiple octaves.
///
[Pure]
public static INoiseField ValueField( Parameters parameters )
{
return new FastNoiseField( parameters is FractalParameters
? FastNoise.NoiseType.ValueFractal
: FastNoise.NoiseType.Value, parameters );
}
///
/// Creates a Perlin noise field,
/// which smoothly samples a grid of random gradients. Use a
/// for the field to have multiple octaves.
///
[Pure]
public static INoiseField PerlinField( Parameters parameters )
{
return new FastNoiseField( parameters is FractalParameters
? FastNoise.NoiseType.PerlinFractal
: FastNoise.NoiseType.Perlin, parameters );
}
///
/// Creates a Simplex noise field,
/// a cheaper gradient noise function similar to . Use a
/// for the field to have multiple octaves.
///
[Pure]
public static INoiseField SimplexField( Parameters parameters )
{
return new FastNoiseField( parameters is FractalParameters
? FastNoise.NoiseType.SimplexFractal
: FastNoise.NoiseType.Simplex, parameters );
}
}
file sealed class FastNoiseField : INoiseField
{
private readonly FastNoise.NoiseType _type;
private readonly Noise.Parameters _parameters;
private readonly FastNoise _impl;
internal FastNoiseField( FastNoise.NoiseType type, Noise.Parameters parameters )
{
_type = type;
_parameters = parameters;
_impl = new FastNoise( parameters.Seed );
_impl.SetNoiseType( type );
_impl.SetFrequency( parameters.Frequency );
if ( parameters is Noise.FractalParameters fractalParameters )
{
_impl.SetFractalOctaves( fractalParameters.Octaves );
_impl.SetFractalGain( fractalParameters.Gain );
_impl.SetFractalLacunarity( fractalParameters.Lacunarity );
}
}
float INoiseField.Sample( float x, float y ) => Noise.ConvertRange( _impl.GetNoise( x, y ) );
float INoiseField.Sample( float x, float y, float z ) => Noise.ConvertRange( _impl.GetNoise( x, y, z ) );
public override string ToString()
{
return $"{_type} {_parameters}";
}
}