//Copyright(c)2020 Procedural Worlds Pty Limited using System.Collections.Generic; using UnityEngine; #if UNITY_EDITOR using UnityEditor; #endif namespace GeNa.Core { public class RiverFlow2 : MonoBehaviour { [HideInInspector] public List m_startPositions = new List(); [Header("Calculation Parameters")] public bool m_selectTerrainOnly = true; [Min(0.0f)] public float m_seaLevel = 25.0f; [Range(0.05f, 3.0f)] public float m_startDepth = 0.15f; [Range(2.0f, 8.0f)] public float m_minNodeDistance = 2.0f; [Range(1.0f, 8.0f)] public float m_connectSnapDistance = 4.0f; public bool m_autoAnalyze = true; [Header("Simplification Parameters")] [Range(0.5f, 1.5f)] public float m_yScale = 1.0f; [Range(0.5f, 2.5f)] public float m_simplifyEpsilon = 1.25f; public bool m_showOriginalPath = true; List> m_waterFlows = new List>(); [HideInInspector] public List> m_simplifiedWaterFlows = new List>(); List m_searchSpaces = new List(); List<(Vector3 from, Vector3 to)> m_tryConnects = new List<(Vector3 from, Vector3 to)>(); [HideInInspector] public float selectionDiameter = 3.0f; private void OnEnable() { m_seaLevel = GeNaEvents.GetSeaLevel(m_seaLevel); } public void UpdateFlows() { if (m_autoAnalyze) { #if UNITY_EDITOR CreateRiverFlowsClicked(); #endif } } public void CreateRiverFlowsClicked() { if (m_startPositions.Count == 0) return; ClearOutputs(); m_searchSpaces = null; m_tryConnects = null; WaterFlow._raycastTerrainOnly = m_selectTerrainOnly; WaterFlow._checkDistance = m_minNodeDistance; WaterFlow._attractDistance = m_connectSnapDistance; m_waterFlows = WaterFlow.FindFlows(m_startPositions, m_startDepth, m_seaLevel, m_searchSpaces, m_tryConnects); Vector3 scale = new Vector3(1.0f, m_yScale, 1.0f); m_simplifiedWaterFlows = WaterFlow.SimplifyFlows(m_waterFlows, scale, m_simplifyEpsilon); } public void CreateSplinesClicked() { CreateRiverSpline(m_simplifiedWaterFlows); } public void ClearClicked() { m_startPositions.Clear(); ClearOutputs(); } public void AddPoint(Vector3 point) { int index = GetPointIndex(point); if (index < 0) { m_startPositions.Add(point); CheckAutoAnalyze(true); } } public void RemovePoint(Vector3 point) { int index = GetPointIndex(point); if (index >= 0) { m_startPositions.RemoveAt(index); CheckAutoAnalyze(true); } } private void ClearOutputs() { m_waterFlows.Clear(); m_simplifiedWaterFlows.Clear(); if (m_searchSpaces != null) m_searchSpaces.Clear(); if (m_tryConnects != null) m_tryConnects.Clear(); } private List CreateFlowNodes() { List startNodes = new List(); // Make sure they are on the ground. for (int i = 0; i < m_startPositions.Count; i++) { startNodes.Add(m_startPositions[i]); } return startNodes; } private void CheckAutoAnalyze(bool pointAdded) { if (m_autoAnalyze && pointAdded) { CreateRiverFlowsClicked(); } } private int GetPointIndex(Vector3 point) { for (int i = 0; i < m_startPositions.Count; i++) { if ((m_startPositions[i] - point).magnitude <= selectionDiameter) return i; } return -1; } private void CreateRiverSpline(List> flows) { Spline spline = Spline.CreateSpline("River Spline"); foreach (List waterNodes in flows) { GeNaNode prevNode = spline.CreateNewNode(waterNodes[0].m_position); spline.AddNode(prevNode); waterNodes[0].m_id = prevNode.ID; for (int i = 1; i < waterNodes.Count; i++) { GeNaNode nextNode = null; if (waterNodes[i].m_connected != null && waterNodes[i].m_connected.m_id != 0) { nextNode = spline.GetNode(waterNodes[i].m_connected.m_id); if (nextNode != null) { spline.AddCurve(prevNode, nextNode); prevNode = nextNode; waterNodes[i].m_id = nextNode.ID; continue; } } nextNode = spline.CreateNewNode(waterNodes[i].m_position); waterNodes[i].m_id = nextNode.ID; spline.AddCurve(prevNode, nextNode); prevNode = nextNode; } } GeNaCarveExtension carve = spline.AddExtension(); if (carve != null) { carve.name = "Carve"; carve.HeightOffset = -0.8f; carve.Width = 4f; carve.Shoulder = 3.5f; carve.MaskFractal.Enabled = true; carve.MaskFractal.Strength = 0.2f; carve.MaskFractal.Octaves = 4; carve.MaskFractal.Lacunarity = 2.5f; } GeNaClearDetailsExtension clearDetails = spline.AddExtension(); if (clearDetails != null) { clearDetails.name = "Clear Details/Grass"; clearDetails.Width = 4.0f; clearDetails.Shoulder = 1.5f; } GeNaClearTreesExtension clearTrees = spline.AddExtension(); if (clearTrees != null) { clearTrees.name = "Clear Trees"; clearTrees.Width = 4.0f; clearTrees.Shoulder = 1.5f; } GeNaTerrainExtension terrainTexture = spline.AddExtension(); if (terrainTexture != null) { terrainTexture.name = "Texture"; terrainTexture.Width = 4.0f; terrainTexture.Shoulder = 0.75f; terrainTexture.EffectType = EffectType.Texture; } GeNaRiverExtension river = spline.AddExtension(); if (river != null) { river.name = "River"; river.RiverProfile = GeNaUtility.LoadNewRiverProfile(); spline.Smooth(); } GeNaSpawnerExtension spawner = spline.AddExtension(); GameObject spawnerObject = Resources.Load("Prefabs/Spawners/Spawner - Reflection Probe"); if (spawnerObject != null) { spawner.Spawner = spawnerObject.GetComponent(); spawner.FlowRate = 30f; } spline.UpdateSpline(); #if UNITY_EDITOR Selection.activeGameObject = spline.gameObject; #endif } private void DisplayWaterFlowsGizmos(List> flows, Color lineColor, bool showAxis) { foreach (List flowNodes in flows) { if (flowNodes.Count > 0) { if (showAxis) { Gizmos.color = Color.blue; Gizmos.DrawRay(flowNodes[0].m_position, Vector3.forward * 3.0f); Gizmos.color = Color.red; Gizmos.DrawRay(flowNodes[0].m_position, Vector3.right * 3.0f); Gizmos.color = Color.yellow; Gizmos.DrawRay(flowNodes[0].m_position, Vector3.up * 3.0f); } for (int i = 0; i < flowNodes.Count; i++) { if (flowNodes[i].m_connected != null) Gizmos.color = Color.blue; else Gizmos.color = Color.green; Gizmos.DrawSphere(flowNodes[i].m_position, flowNodes[i].m_depth); Gizmos.DrawRay(flowNodes[i].m_position, flowNodes[i].m_direction * 1.0f); if (i < flowNodes.Count - 1) { Gizmos.color = lineColor; Gizmos.DrawLine(flowNodes[i].m_position, flowNodes[i + 1].m_position); } } } } } private void OnDrawGizmosSelected() { if (m_waterFlows == null) return; if (m_showOriginalPath) DisplayWaterFlowsGizmos(m_waterFlows, Color.magenta, false); DisplayWaterFlowsGizmos(m_simplifiedWaterFlows, Color.yellow, true); if (m_searchSpaces == null) return; Gizmos.color = Color.red; for (int i = 0; i < m_searchSpaces.Count; i++) { Gizmos.DrawWireSphere(m_searchSpaces[i], 0.13f); } if (m_tryConnects == null) return; for (int i = 0; i < m_tryConnects.Count; i++) { Gizmos.color = Color.blue; if (m_tryConnects[i].from.y == m_tryConnects[i].to.y) Gizmos.color = Color.white; Gizmos.DrawLine(m_tryConnects[i].from, m_tryConnects[i].to); Gizmos.color = Color.green; Vector3 direction = (m_tryConnects[i].to - m_tryConnects[i].from); float halfDist = direction.magnitude * 0.5f; direction = direction.normalized; Gizmos.DrawRay(m_tryConnects[i].from, direction * halfDist); } } } }