Files
sbox-public/engine/Sandbox.Engine/Resources/Textures/Generators/RandomTextureGenerator.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

142 lines
3.3 KiB
C#

using Sandbox.Utility;
using System.Threading;
namespace Sandbox.Resources;
[Title( "Random Noise" )]
[Icon( "casino" )]
[ClassName( "random" )]
[Expose]
public class RandomTextureGenerator : TextureGenerator
{
public enum NoiseType
{
[Icon( "casino" )]
[Title( "Pure Random" )]
Random,
[Icon( "gradient" )]
[Title( "Perlin Noise" )]
Perlin,
[Icon( "gradient" )]
[Title( "Simplex Noise" )]
Simplex
}
public NoiseType Type { get; set; } = NoiseType.Random;
public int Seed { get; set; } = 0;
public Vector2Int Size { get; set; } = 64;
[HideIf( nameof( Type ), NoiseType.Random )]
public Vector3 Offset { get; set; } = new Vector3( 0, 0, 0 );
[HideIf( nameof( Type ), NoiseType.Random )]
public float Scale { get; set; } = 5.0f;
[Range( 1, 8 )]
[HideIf( nameof( Type ), NoiseType.Random )]
public int Octaves { get; set; } = 1;
public Gradient Gradient { get; set; } = new Gradient( new Gradient.ColorFrame( 0.0f, Color.Black ), new Gradient.ColorFrame( 1.0f, Color.White ) );
[Header( "Normal Map" ), Title( "Height to Normal" )]
public bool ConvertHeightToNormals { get; set; }
[ShowIf( nameof( ConvertHeightToNormals ), true )]
public float NormalScale { get; set; } = 1;
[Hide]
public override bool CacheToDisk => true;
protected override async ValueTask<Texture> CreateTexture( Options options, CancellationToken ct )
{
var w = Size.x.Clamp( 1, 1024 * 4 );
var h = Size.y.Clamp( 1, 1024 * 4 );
var scale = (Type != NoiseType.Random) ? Scale : 1f;
var pixels = new Color[w * h];
long seed = Seed;
INoiseField noise = null;
switch ( Type )
{
case NoiseType.Perlin:
if ( Octaves > 1 )
{
// Use fractal noise field for multiple octaves
noise = Noise.PerlinField( new Noise.FractalParameters(
Seed: Seed,
Octaves: Octaves
) );
}
else
{
noise = Noise.PerlinField( new Noise.Parameters( Seed ) );
}
break;
case NoiseType.Simplex:
if ( Octaves > 1 )
{
// Use fractal noise field for multiple octaves
noise = Noise.SimplexField( new Noise.FractalParameters(
Seed: Seed,
Octaves: Octaves
) );
}
else
{
noise = Noise.SimplexField( new Noise.Parameters( Seed ) );
}
break;
default:
seed = (long)(IntToRandomFloat( Seed ) * int.MaxValue);
break;
}
await Sandbox.Utility.Parallel.ForAsync( 0, w * h, ct, ( i, t ) =>
{
var x = i % w;
var y = i / w;
var noiseValue = 0f;
if ( noise is not null )
{
noiseValue = noise.Sample( (x + Offset.x) * scale, (y + Offset.y) * scale, Offset.z * scale );
}
else
{
noiseValue = IntToRandomFloat( seed + i );
}
pixels[i] = Gradient.Evaluate( noiseValue );
return ValueTask.CompletedTask;
} );
var bitmap = new Bitmap( w, h, false );
bitmap.SetPixels( pixels );
if ( ConvertHeightToNormals )
{
bitmap = bitmap.HeightmapToNormalMap( NormalScale );
}
ct.ThrowIfCancellationRequested();
return bitmap.ToTexture();
}
public static float IntToRandomFloat( long seed )
{
unchecked
{
// Multiply the seed by a large prime number
seed = (seed ^ 0x6D2B79F5) * 0x1B873593;
seed = (seed ^ (seed >> 13)) * 0x85EBCA6B;
seed = seed ^ (seed >> 16);
// Normalize to a float in the range [0, 1)
return (seed & 0x7FFFFFFF) / (float)int.MaxValue;
}
}
}