Effects/PerlinVolumeTexture.cs
using System;
using Sandbox.Utility;

/// <summary>
/// Procedurally builds a 3‑dimensional Perlin noise volume texture for use in materials and shaders.
/// </summary>
public sealed class PerlinVolumeTexture
{
	/// <summary>Width of the 3D texture in voxels.</summary>
	public int Width { get; }
	/// <summary>Height of the 3D texture in voxels.</summary>
	public int Height { get; }
	/// <summary>Depth of the 3D texture in voxels.</summary>
	public int Depth { get; }

	/// <summary>
	/// Spatial scale applied to the noise coordinates. Higher values zoom out; lower values zoom in.
	/// </summary>
	public float Scale { get; set; } = 1.0f;

	/// <summary>Number of Fractal Brownian Motion octaves layered together.</summary>
	public int Octaves { get; set; } = 4;

	/// <summary>Resource name given to the resulting <see cref="Texture"/>.</summary>
	public string Name { get; set; }

	/// <summary>
	/// Creates a new Perlin 3D texture generator.
	/// </summary>
	public PerlinVolumeTexture( int width, int height, int depth, string name = null )
	{
		if ( width <= 0 || height <= 0 || depth <= 0 )
			throw new ArgumentOutOfRangeException( "Texture dimensions must be positive non‑zero." );

		Width = width;
		Height = height;
		Depth = depth;
		Name = name ?? $"perlin_volume_{width}x{height}x{depth}";
	}

	/// <summary>
	/// Generates the noise on the CPU and uploads the data to a GPU.
	/// </summary>
	public Texture Generate()
	{
		var buffer = new byte[Width * Height * Depth];
		var idx = 0;

		for ( int z = 0; z < Depth; z++ )
		{
			for ( int y = 0; y < Height; y++ )
			{
				for ( int x = 0; x < Width; x++ )
				{
					// Noise.Fbm returns [0,1]. Convert to byte range.
					float n = Noise.Fbm( Octaves, x * Scale, y * Scale, z * Scale );
					buffer[idx++] = (byte)(n * 255f);
				}
			}
		}

		return Texture.CreateVolume( Width, Height, Depth, ImageFormat.I8 )
					 .WithName( Name )
					 .WithData( buffer )
					 .Finish();
	}
}