using System; using System.Collections.Generic; using System.Linq; using Moonlit.Core; using UltimateWater.Internal; using UnityEngine; namespace UltimateWater { public class River : MonoBehaviour { [Serializable] private struct Vertex { public Vector2 Position; public float Height; public Vector2 Normal; public Vector2 Uv; } [Serializable] public class Node { public Vector2 Position; public float Width; public float Speed; public float Height; public float Distance; } [Header("References")] [SerializeField] [HideInInspector] private List _Nodes; [SerializeField] private Water _Water; [Header("Settings")] [SerializeField] [Tooltip("How fast the river flows")] private float _Speed = 1f; [SerializeField] [Tooltip("Base width of the river, adjusted by nodes")] private float _Width = 10f; [SerializeField] [Tooltip("Mesh density")] private int _Segments = 4; [SerializeField] private float _ScaleX = 1f; [SerializeField] private float _ScaleY = 1f; [SerializeField] [Range(0f, 1f)] [Tooltip("Normalizes horizontal uv to match the width modified by nodes")] private float _Normalize = 1f; [Header("Debug")] [SerializeField] private bool _DrawMesh = true; private WaterSample sample; [SerializeField] [HideInInspector] private float _Length; [SerializeField] [HideInInspector] private List _InterpolatedPath; [SerializeField] [HideInInspector] private List _Vertices; public Vector3 GetCurrentForce(Vector3 point) { if (_InterpolatedPath == null || _InterpolatedPath.Count < 2) { return Vector3.zero; } int num = -1; float num2 = float.MaxValue; for (int i = 0; i < _InterpolatedPath.Count; i++) { float num3 = Vector2.Distance(new Vector2(point.x, point.z), _InterpolatedPath[i].Position); if (num3 < _Width * 0.5f && num3 < num2) { num = i; num2 = num3; } } if (num == -1) { return Vector3.zero; } if (num == _InterpolatedPath.Count - 1) { Vector2 vector = -(_InterpolatedPath[num].Position - _InterpolatedPath[num - 1].Position).normalized; return new Vector3(vector.x, 0f, vector.y) * _Speed; } Vector2 vector2 = -(_InterpolatedPath[num + 1].Position - _InterpolatedPath[num].Position).normalized; return new Vector3(vector2.x, 0f, vector2.y) * _Speed; } private void OnValidate() { if (_Segments < 2) { _Segments = 2; } if (!(_Water == null)) { _Water.River = this; _Water.Renderer.PropertyBlock.SetFloat("_RiverSpeed", _Speed); } } private void Awake() { if (!(_Water == null)) { _Water.Geometry.GeometryType = WaterGeometry.Type.CustomMeshes; _Water.River = this; } } private void OnEnable() { if (!(_Water == null)) { if (!_Water.ShaderSet.CustomTriangularGeometry) { Debug.LogError("Water ShaderSet should have CustomTriangularGeometry enabled"); base.enabled = false; } else { _Water.Renderer.PropertyBlock.SetFloat("_RiverEnabled", 1f); } } } private void OnDisable() { if (!(_Water == null)) { _Water.Renderer.PropertyBlock.SetFloat("_RiverEnabled", 0f); } } private void OnDrawGizmos() { if (_Water == null) { return; } int count = _Nodes.Count; _Nodes = GetComponentsInChildren().ToList(); _Nodes.Remove(base.transform); foreach (Transform node in _Nodes) { if (node.GetComponent() == null) { node.gameObject.AddComponent(); } } int num = 0; while (num < _Nodes.Count - 1) { Transform obj = _Nodes[num]; if (Mathf.Approximately(Vector3.Distance(b: _Nodes[num + 1].transform.position, a: obj.transform.position), 0f)) { _Nodes.RemoveAt(num + 1); } else { num++; } } if (_Nodes.Count < 2) { return; } if (_Nodes.Count != count) { Transform transform = _Nodes[_Nodes.Count - 1]; for (int i = 0; i < _Nodes.Count - 1; i++) { Transform transform2 = _Nodes[i]; if (Mathf.Approximately(Vector2.Distance(transform.transform.position, transform2.transform.position), 0f)) { int siblingIndex = transform2.GetSiblingIndex(); transform.SetSiblingIndex(siblingIndex + 1); break; } } } List path = _Nodes.Select((Transform n) => n.GetComponent().Node).ToList(); Mesh mesh = Generate(path, _Width, _Segments); _Water.Geometry.CustomSurfaceMeshes.Meshes = new Mesh[1] { mesh }; if (_DrawMesh) { Gizmos.matrix = Matrix4x4.TRS(new Vector3(0f, base.transform.position.y, 0f), Quaternion.identity, Vector3.one); Gizmos.color = new Color(0f, 1f, 0f, 0.2f); Gizmos.DrawWireMesh(_Water.Geometry.CustomSurfaceMeshes.Meshes[0]); } } private void Reset() { Transform parent = base.transform; while (base.transform.childCount != 0) { UnityEngine.Object.DestroyImmediate(base.transform.GetChild(0).gameObject); } GameObject obj = new GameObject("Node"); obj.transform.SetParent(parent); obj.transform.localPosition = Vector3.zero; GameObject obj2 = new GameObject("Node"); obj2.transform.SetParent(parent); obj2.transform.localPosition = Vector3.forward * 10f; } private Mesh Generate(List path, float width, int segments) { int points = Mathf.Max((int)((float)_Segments * Length(path) / width), 2); _InterpolatedPath = CubicSpline.Interpolate(path, (Node p) => p.Position, delegate(Node p, Vector2 r) { p.Position = r; }, points); _Length = Length(_InterpolatedPath); CalculateUVX(path); CalculateUVX(_InterpolatedPath); int num = 0; int num2 = 0; for (; num < _InterpolatedPath.Count; num++) { for (; num2 < path.Count - 1 && path[num2].Distance <= _InterpolatedPath[num].Distance; num2++) { } float width2 = path[num2].Width; float speed = path[num2].Speed; float height = path[num2].Height; if (num2 - 1 >= 0) { Node node = path[num2 - 1]; Node node2 = path[num2]; float num3 = Vector2.Distance(node.Position, _InterpolatedPath[num].Position); float num4 = Vector2.Distance(node2.Position, _InterpolatedPath[num].Position); float t = num3 / (num3 + num4); width2 = Mathf.Lerp(node.Width, node2.Width, t); speed = Mathf.Lerp(node.Speed, node2.Speed, t); height = Mathf.Lerp(node.Height, node2.Height, t); } _InterpolatedPath[num].Width = width2; _InterpolatedPath[num].Speed = speed; _InterpolatedPath[num].Height = height; } _Vertices = GenerateVertices(_InterpolatedPath, width, segments); return GenerateMesh(_Vertices); } private List GenerateVertices(List path, float width, int segments) { List list = new List(); float num = 0f; Vector2 normalized = (path[1].Position - path[0].Position).normalized; Vector2 normalized2 = new Vector2(0f - normalized.y, normalized.x).normalized; float num2 = width / (float)segments; Vertex item = default(Vertex); for (int i = 0; i < segments + 1; i++) { Node node = path[0]; item.Position = node.Position + normalized2 * 0.5f * (width - 2f * num2 * (float)i) * node.Width; item.Height = node.Height; item.Normal = normalized; item.Uv = new Vector2(num, (float)i / (float)segments * Mathf.Lerp(1f, node.Width, _Normalize)); list.Add(item); } Vertex item2 = default(Vertex); for (int j = 1; j < path.Count - 1; j++) { Node node2 = path[j]; normalized = path[j + 1].Position - path[j - 1].Position; normalized2 = new Vector2(0f - normalized.y, normalized.x).normalized; num += node2.Speed / (float)(path.Count - 1); for (int k = 0; k < segments + 1; k++) { item2.Position = node2.Position + normalized2 * 0.5f * (width - 2f * num2 * (float)k) * node2.Width; item2.Normal = normalized; item2.Height = node2.Height; item2.Uv = new Vector2(num, (float)k / (float)segments * Mathf.Lerp(1f, node2.Width, _Normalize)); list.Add(item2); } } normalized = path[path.Count - 1].Position - path[path.Count - 2].Position; normalized2 = new Vector2(0f - normalized.y, normalized.x).normalized; Vertex item3 = default(Vertex); for (int l = 0; l < segments + 1; l++) { Node node3 = path[path.Count - 1]; item3.Position = node3.Position + normalized2 * 0.5f * (width - 2f * num2 * (float)l) * node3.Width; item3.Height = node3.Height; item3.Normal = normalized; item3.Uv = new Vector2(num, (float)l / (float)segments * Mathf.Lerp(1f, node3.Width, _Normalize)); list.Add(item3); } return list; } private Mesh GenerateMesh(List points) { Vector3[] normals = points.Select((Vertex x) => TransformUV(x)).ToArray(); int num = points.Count - (_Segments + 1); List list = new List(); for (int num2 = 0; num2 < num - 1; num2 += _Segments + 1) { for (int num3 = 0; num3 < _Segments; num3++) { list.Add(num2 + num3 + 1); list.Add(num2 + num3); list.Add(num2 + num3 + 1 + _Segments); list.Add(num2 + num3 + 1 + _Segments); list.Add(num2 + num3 + 2 + _Segments); list.Add(num2 + num3 + 1); } } Mesh result = new Mesh { vertices = points.Select((Vertex x) => new Vector3(x.Position.x, 0f, x.Position.y)).ToArray(), normals = normals, triangles = list.ToArray(), uv = points.Select((Vertex x) => x.Uv).ToArray() }; _Water.transform.position = new Vector3(_Water.transform.position.x, base.transform.position.y, _Water.transform.position.z); return result; } private static float Length(List nodes) { return Length(nodes, (Node node) => node.Position); } private static float Length(List nodes, Func getter) { float num = 0f; for (int i = 0; i < nodes.Count - 1; i++) { num += Vector2.Distance(getter(nodes[i]), getter(nodes[i + 1])); } return num; } private static void CalculateUVX(List nodes) { float num = 0f; for (int i = 0; i < nodes.Count - 1; i++) { num += Vector2.Distance(nodes[i].Position, nodes[i + 1].Position); nodes[i + 1].Distance = num; } for (int j = 0; j < nodes.Count; j++) { nodes[j].Distance /= num; } } private Vector3 TransformUV(Vertex vertex) { return new Vector3(vertex.Uv.x * _ScaleX * _Length, 0f, (0f - vertex.Uv.y) * _ScaleY * _Width); } } }