Network/WaterNetwork.Settings.cs

Settings portion of the WaterNetwork component, defining editable properties for water rendering, flow and performance and providing helpers to resolve material/profile and compute foam amount.

File Access
using System;
using Sandbox;

namespace RedSnail.WaterTool2;

public sealed partial class WaterNetwork
{
	public const string DefaultMaterialPath = "watertool2/materials/lakewater.vmat";

	/// <summary>
	/// The water material every river and lake of the network is drawn with (an advancedwater.shader material). Connected
	/// water is one surface, so it is one material; the default lake water when empty.
	/// </summary>
	[Property, Group("Surface"), Order(0)] public Material Material { get; set; }

	/// <summary>
	/// The waves every river and lake of the network moves with; each scales them by its own wave strength, so the
	/// whole network shares one continuous wave field.
	/// </summary>
	[Property(Title = "Wave Profile"), Group("Surface"), Order(0)] public WaterDefinition WaveProfile { get; set; }

	/// <summary>
	/// How far apart the water's vertices are. Smaller follows the waves more closely and costs more.
	/// </summary>
	[Property(Title = "Cell Size"), Group("Surface"), Order(0), Range(16.0f, 256.0f)]
	public float CellSize
	{
		get;
		set
		{
			field = value.Clamp(8.0f, 2048.0f);
			MarkDirty();
		}
	} = 48.0f;

	/// <summary>
	/// Scales the normal maps' tiling.
	/// </summary>
	[Property(Title = "Texture Scale"), Group("Surface"), Order(0), Range(0.1f, 4.0f)] public float TextureScale { get; set; } = 1.0f;

	/// <summary>
	/// The depth of water at which the material's deep colour has fully taken over.
	/// </summary>
	[Property(Title = "Colour Depth"), Group("Surface"), Order(0)] public float ColorDepth { get; set; } = 400.0f;

	/// <summary>
	/// How much a slope speeds a river up: at a drop of 1 in 10 the water runs this many tenths faster than on the flat.
	/// </summary>
	[Property(Title = "Slope Speed-Up"), Group("Flow"), Order(1), Range(0.0f, 40.0f)]
	public float SlopeSpeedup
	{
		get;
		set
		{
			field = value.Clamp(0.0f, 1000.0f);
			MarkDirty();
		}
	} = 8.0f;

	/// <summary>
	/// How far the water takes to settle to a new speed: through a confluence, onto a slope, back onto the flat.
	/// </summary>
	[Property(Title = "Speed Smoothing"), Group("Flow"), Order(1), Range(50.0f, 5000.0f)]
	public float SpeedSmoothing
	{
		get;
		set
		{
			field = value.Clamp(1.0f, 100000.0f);
			MarkDirty();
		}
	} = 800.0f;

	[Property(Title = "Max Speed"), Group("Flow"), Order(1)]
	public float MaxSpeed
	{
		get;
		set
		{
			field = value.Clamp(1.0f, 100000.0f);
			MarkDirty();
		}
	} = 1500.0f;

	/// <summary>
	/// How much the water slows towards the banks: 0 runs as fast at the bank as in the middle, 0.5 half as fast.
	/// </summary>
	[Property(Title = "Bank Drag"), Group("Flow"), Order(1), Range(0.0f, 0.9f)]
	public float BankDrag
	{
		get;
		set
		{
			field = value.Clamp(0.0f, 0.95f);
			MarkDirty();
		}
	} = 0.3f;

	/// <summary>
	/// How long, in seconds, flowing water carries its waves and textures before the cycle starts over (the restart is
	/// always cross-faded away). Longer travels further between restarts but stretches more in fast or bending water.
	/// </summary>
	[Property(Title = "Flow Cycle"), Group("Flow"), Order(1), Range(0.5f, 8.0f)] public float FlowCycle { get; set { field = value.Clamp(0.1f, 60.0f); } } = 2.0f;

	/// <summary>
	/// How far a river's current carries on into a lake, in river widths.
	/// </summary>
	[Property(Title = "Plume Length"), Group("Flow"), Order(1), Range(0.5f, 20.0f)]
	public float PlumeLength
	{
		get;
		set
		{
			field = value.Clamp(0.1f, 100.0f);
			MarkDirty();
		}
	} = 4.0f;

	/// <summary>
	/// How far, in river widths, a river's wave strength takes to blend into the lake (or the next river) it runs into.
	/// </summary>
	[Property(Title = "Wave Blend"), Group("Flow"), Order(1), Range(0.0f, 10.0f)]
	public float WaveBlend
	{
		get;
		set
		{
			field = value.Clamp(0.0f, 100.0f);
			MarkDirty();
		}
	} = 1.5f;

	/// <summary>
	/// How much foam fast water churns up, at most.
	/// </summary>
	[Property(Title = "Foam"), Group("Foam"), Order(2), Range(0.0f, 1.0f)]
	public float FoamAmount
	{
		get;
		set
		{
			field = value.Clamp(0.0f, 1.0f);
			MarkDirty();
		}
	} = 0.6f;

	/// <summary>
	/// The speed water starts to foam at.
	/// </summary>
	[Property(Title = "Foam Start Speed"), Group("Foam"), Order(2)]
	public float FoamStartSpeed
	{
		get;
		set
		{
			field = value.Clamp(0.0f, 100000.0f);
			MarkDirty();
		}
	} = 350.0f;

	/// <summary>
	/// The speed water is fully foaming at.
	/// </summary>
	[Property(Title = "Foam Full Speed"), Group("Foam"), Order(2)]
	public float FoamFullSpeed
	{
		get;
		set
		{
			field = value.Clamp(0.0f, 100000.0f);
			MarkDirty();
		}
	} = 900.0f;

	/// <summary>
	/// A post-process volume (fog, wobble) switched on while the camera is under the water.
	/// </summary>
	[Property(Title = "Underwater Volume"), Group("Post Processing"), Order(3)] public PostProcessVolume UnderwaterPostProcessVolume { get; set; }

	/// <summary>
	/// Skips every piece of water outside the camera's view.
	/// </summary>
	[Property(Title = "Frustum Culling"), Group("Performance"), Order(4)] public bool FrustumCulling { get; set; } = true;

	/// <summary>
	/// Slack added round each piece before the frustum test, so pieces at the screen's edge don't pop.
	/// </summary>
	[Property(Title = "Cull Padding"), Group("Performance"), Order(4)] public float CullPadding { get; set; } = 256.0f;

	/// <summary>
	/// Pieces further than this are skipped. 0 for no limit.
	/// </summary>
	[Property(Title = "Max Render Distance"), Group("Performance"), Order(4)] public float MaxRenderDistance { get; set; } = 30000.0f;

	/// <summary>
	/// Rivers are built in pieces of about this length: a change only rebuilds the pieces it touches, and only the
	/// pieces in view are drawn.
	/// </summary>
	[Property(Title = "River Chunk Length"), Group("Performance"), Order(4)]
	public float ChunkLength
	{
		get;
		set
		{
			field = value.Clamp(256.0f, 100000.0f);
			MarkDirty();
		}
	} = 3000.0f;

	/// <summary>
	/// Lakes are cut into square tiles of this size, so only the parts in view are drawn.
	/// </summary>
	[Property(Title = "Lake Tile Size"), Group("Performance"), Order(4)]
	public float LakeTileSize
	{
		get;
		set
		{
			field = value.Clamp(256.0f, 100000.0f);
			MarkDirty();
		}
	} = 2048.0f;

	private Material m_FallbackMaterial;



	/// <summary>
	/// The material the water is drawn with: the network's own, or the default lake water.
	/// </summary>
	public Material ResolvedMaterial
	{
		get
		{
			if (Material.IsValid())
				return Material;

			m_FallbackMaterial ??= Sandbox.Material.Load(DefaultMaterialPath);

			return m_FallbackMaterial;
		}
	}



	/// <summary>
	/// The wave profile the water moves with: the network's own, or gentle lake waves.
	/// </summary>
	public WaterDefinition ResolvedProfile => WaveProfile.IsValid() ? WaveProfile : WaterDefinition.Fallback;



	/// <summary>
	/// How much foam water running at a speed has.
	/// </summary>
	internal float FoamFor(float _Speed)
	{
		if (FoamAmount <= 0.0f)
			return 0.0f;

		float range = MathF.Max(1.0f, FoamFullSpeed - FoamStartSpeed);
		float t = Math.Clamp((_Speed - FoamStartSpeed) / range, 0.0f, 1.0f);

		return FoamAmount * t * t * (3.0f - 2.0f * t);
	}
}