387 lines
10 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|