Editor utility for drawing water-related gizmos in the editor. It renders bezier curves, river outlines, flow direction arrows, node markers, polylines and lake shores using provided WaterCurve, RiverWidth, WaterNetwork and Transform data.
using System;
using System.Collections.Generic;
using Sandbox;
namespace RedSnail.WaterTool2.Editor;
/// <summary>
/// Gizmo drawing shared by the water tools. Everything is given in network-local space and drawn through the
/// network's transform, in whatever colour and thickness the caller set up.
/// </summary>
internal static class WaterGizmos
{
public static void DrawCurve(in WaterCurve _Curve, in Transform _Transform, int _Steps = 24)
{
Vector3 previous = _Transform.PointToWorld(_Curve.P0);
for (int i = 1; i <= _Steps; i++)
{
Vector3 point = _Transform.PointToWorld(_Curve.Evaluate((float)i / _Steps));
Gizmo.Draw.Line(previous, point);
previous = point;
}
}
/// <summary>
/// The two banks of a river along its curve (the curve's parameter standing in for its distance), capped at both
/// ends.
/// </summary>
public static void DrawRiverOutline(in WaterCurve _Curve, in RiverWidth _Width, in Transform _Transform, int _Steps = 24)
{
Vector3 previousLeft = default;
Vector3 previousRight = default;
for (int i = 0; i <= _Steps; i++)
{
float t = (float)i / _Steps;
float half = _Width.HalfAt(t * _Width.Length);
Vector3 point = _Curve.Evaluate(t);
Vector3 right = WaterCurve.RightOf(_Curve.Tangent(t));
Vector3 left = _Transform.PointToWorld(point - right * half);
Vector3 rightBank = _Transform.PointToWorld(point + right * half);
if (i == 0 || i == _Steps)
Gizmo.Draw.Line(left, rightBank);
if (i > 0)
{
Gizmo.Draw.Line(previousLeft, left);
Gizmo.Draw.Line(previousRight, rightBank);
}
previousLeft = left;
previousRight = rightBank;
}
}
/// <summary>
/// Arrows down a river's centreline, every <paramref name="_Spacing"/> or so, pointing the way its water flows.
/// </summary>
public static void DrawFlowArrows(in WaterCurve _Curve, in Transform _Transform, float _Spacing, float _Size)
{
float length = _Curve.ApproximateLength(16);
int arrows = Math.Clamp((int)(length / MathF.Max(1.0f, _Spacing)), 1, 64);
for (int i = 0; i < arrows; i++)
{
float t = (i + 0.5f) / arrows;
Vector3 point = _Curve.Evaluate(t);
Vector3 tangent = _Curve.Tangent(t);
Vector3 from = _Transform.PointToWorld(point - tangent * _Size * 0.5f);
Vector3 to = _Transform.PointToWorld(point + tangent * _Size * 0.5f);
Gizmo.Draw.Arrow(from, to, _Size * 0.35f, _Size * 0.2f);
}
}
/// <summary>
/// A node marker: a flat ring, filled when it is the one being worked on.
/// </summary>
public static void DrawNode(Vector3 _WorldPosition, bool _Filled)
{
float radius = WaterPicker.NodeRadius(_WorldPosition);
if (_Filled)
Gizmo.Draw.SolidSphere(_WorldPosition, radius * 0.6f);
Gizmo.Draw.LineCircle(_WorldPosition, Vector3.Up, radius);
}
public static void DrawPolyline(IReadOnlyList<Vector3> _Points, in Transform _Transform, bool _Closed)
{
if (_Points is null || _Points.Count < 2)
return;
int count = _Closed ? _Points.Count : _Points.Count - 1;
for (int i = 0; i < count; i++)
Gizmo.Draw.Line(_Transform.PointToWorld(_Points[i]), _Transform.PointToWorld(_Points[(i + 1) % _Points.Count]));
}
/// <summary>
/// A lake's shore, or its control polygon when the shore can't be built (too few points).
/// </summary>
public static void DrawLake(WaterNetwork _Network, WaterLake _Lake, in Transform _Transform)
{
var shore = _Network.GetShore(_Lake);
if (shore is not null)
{
DrawPolyline(shore.Points, _Transform, true);
return;
}
var points = new List<Vector3>();
for (int i = 0; i < _Lake.Points.Count; i++)
points.Add(_Lake.PointAt(i));
DrawPolyline(points, _Transform, true);
}
}