Files
2026-03-04 10:03:45 +08:00

387 lines
10 KiB
C#

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<Transform> _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<Node> _InterpolatedPath;
[SerializeField]
[HideInInspector]
private List<Vertex> _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<Transform>().ToList();
_Nodes.Remove(base.transform);
foreach (Transform node in _Nodes)
{
if (node.GetComponent<RiverNode>() == null)
{
node.gameObject.AddComponent<RiverNode>();
}
}
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<Node> path = _Nodes.Select((Transform n) => n.GetComponent<RiverNode>().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<Node> 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<Vertex> GenerateVertices(List<Node> path, float width, int segments)
{
List<Vertex> list = new List<Vertex>();
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<Vertex> points)
{
Vector3[] normals = points.Select((Vertex x) => TransformUV(x)).ToArray();
int num = points.Count - (_Segments + 1);
List<int> list = new List<int>();
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<Node> nodes)
{
return Length(nodes, (Node node) => node.Position);
}
private static float Length<T>(List<T> nodes, Func<T, Vector2> 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<Node> 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);
}
}
}