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}"; } }