RiverModeler.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. // River Modeler
  2. // Staggart Creations (http://staggart.xyz)
  3. // Copyright protected under Unity Asset Store EULA
  4. // Copying or referencing source code for the production of new asset store content is strictly prohibited.
  5. using System;
  6. using UnityEngine;
  7. using UnityEngine.Events;
  8. using UnityEngine.Rendering;
  9. #if MATHEMATICS
  10. using Unity.Mathematics;
  11. #endif
  12. #if SPLINES
  13. using UnityEngine.Splines;
  14. #endif
  15. namespace sc.modeling.river.runtime
  16. {
  17. /// <summary>
  18. /// Front-end MonoBehaviour component
  19. /// </summary>
  20. [AddComponentMenu("Water/River Modeler")]
  21. [HelpURL("https://staggart.xyz/river-modeler-docs/?section=river-modeler")]
  22. [ExecuteInEditMode]
  23. public partial class RiverModeler : MonoBehaviour
  24. {
  25. /// <summary>
  26. /// Flags used to indicate which aspects of a river is being modified
  27. /// </summary>
  28. [Flags]
  29. public enum ChangeFlags
  30. {
  31. All = 1,
  32. Spline = 2,
  33. Geometry = 4,
  34. VertexAttribute = 8,
  35. Foam = 16,
  36. Scale = 32,
  37. Transparency = 64,
  38. Data = 128
  39. }
  40. [Flags]
  41. public enum RebuildTriggers
  42. {
  43. [InspectorName("Via scripting")]
  44. None = 0,
  45. OnSplineChanged = 1,
  46. OnSplineAdded = 2,
  47. OnSplineRemoved = 4,
  48. [InspectorName("On Start()")]
  49. OnStart = 8,
  50. OnUIChange = 16,
  51. OnSplineTransformChange = 32
  52. }
  53. [Tooltip("Control which sort of events cause the mesh to be regenerated." +
  54. "\n\n" +
  55. "For instance when the spline changes (default), or on the component's Start() function." +
  56. "\n\n" +
  57. "If none are selected you need to call the Rebuild() function through script.")]
  58. public RebuildTriggers rebuildTriggers = RebuildTriggers.OnSplineAdded | RebuildTriggers.OnSplineRemoved | RebuildTriggers.OnSplineChanged | RebuildTriggers.OnUIChange | RebuildTriggers.OnSplineTransformChange;
  59. [Tooltip("The target mesh filter the generated mesh is stored in. Attaching a Mesh Renderer to the same object will render it using a material")]
  60. public MeshFilter meshFilter;
  61. [Range(-3, 3)]
  62. [Tooltip("Sets the rendering offset for the Mesh Renderer component." +
  63. "\n\n" +
  64. "This relates directly to transparency sorting and may be used to force one river to render on top of another one." +
  65. "\n\n" +
  66. "For this to have any effect, the renderer must used a shader that doesn't write to the depth buffer (ZWrite Off) ")]
  67. public int orderInLayer = 0;
  68. /* LOD Group support WIP
  69. public LODGroup lodGroup;
  70. [Serializable]
  71. public class LOD
  72. {
  73. [Range(5f, 100f)]
  74. public float resolution = 100f;
  75. public LOD(float resolution)
  76. {
  77. this.resolution = resolution;
  78. }
  79. }
  80. public List<LOD> lods = new List<LOD>()
  81. {
  82. new LOD(100f)
  83. };
  84. */
  85. /// <summary>
  86. /// Settings used for geometry generation
  87. /// </summary>
  88. public Settings settings = new Settings();
  89. public bool exportToFBX;
  90. public string fbxFilePath;
  91. #pragma warning disable CS0067
  92. public delegate void OnRebuildRiver(RiverModeler instance, ChangeFlags updateFlags);
  93. public static event OnRebuildRiver onPreRebuildRiver;
  94. public static event OnRebuildRiver onPostRebuildRiver;
  95. /// <summary>
  96. /// UnityEvent for a GameObject's function to be executed when river is rebuild. This is exposed in the inspector.
  97. /// </summary>
  98. [Serializable]
  99. public class RebuildEvent : UnityEvent<ChangeFlags> { }
  100. /// <summary>
  101. /// UnityEvent, fires whenever spline is rebuild (eg. editing nodes)
  102. /// </summary>
  103. public RebuildEvent onPreRebuild;
  104. public RebuildEvent onPostRebuild;
  105. #pragma warning restore CS0067
  106. public void Reset()
  107. {
  108. #if !SPLINES
  109. Debug.LogError("This component requires the Splines package. Please install this through the Package Manager");
  110. #endif
  111. #if !MATHEMATICS
  112. Debug.LogError("This component requires the Mathematics package. Please install this through the Package Manager");
  113. #endif
  114. meshFilter = GetComponent<MeshFilter>();
  115. #if SPLINES && MATHEMATICS
  116. if(!splineContainer) splineContainer = GetComponentInParent<SplineContainer>();
  117. ValidateData(splineContainer);
  118. #endif
  119. Rebuild();
  120. }
  121. private void OnEnable()
  122. {
  123. #if SPLINES
  124. SubscribeSplineCallbacks();
  125. #endif
  126. }
  127. private void OnDisable()
  128. {
  129. #if SPLINES
  130. UnsubscribeSplineCallbacks();
  131. #endif
  132. }
  133. private void Start()
  134. {
  135. //In this case the component was likely copied somewhere, or prefabbed. Mesh data will have been lost, so regenerating it is an alternative
  136. if (rebuildTriggers.HasFlag(RebuildTriggers.OnStart)) Rebuild();
  137. }
  138. #if SPLINES
  139. private partial void SubscribeSplineCallbacks();
  140. private partial void UnsubscribeSplineCallbacks();
  141. #endif
  142. public void Rebuild(ChangeFlags updateFlags = ChangeFlags.All)
  143. {
  144. //Debug.Log($"Rebuilding river with update flags: {updateFlags}", this);
  145. if (!meshFilter) return;
  146. onPreRebuildRiver?.Invoke(this, updateFlags);
  147. onPreRebuild?.Invoke(updateFlags);
  148. settings.foam.Validate();
  149. #if SPLINES && MATHEMATICS
  150. if (!splineContainer) return;
  151. ValidateData(splineContainer);
  152. if(updateFlags.HasFlag(ChangeFlags.All | ChangeFlags.Spline)) ValidateData(splineContainer);
  153. /* LOD Group support WIP
  154. //Single mesh
  155. if (lods.Count == 1 && meshFilter)
  156. {
  157. meshFilter.sharedMesh = Geometry.Create(splineContainer, ScaleData, OpacityData, generationSettings, meshFilter.transform.worldToLocalMatrix);
  158. }
  159. //LOD set up
  160. if (lods.Count > 1 && lodGroup)
  161. {
  162. UnityEngine.LOD[] lodGroupLODS = lodGroup.GetLODs();
  163. for (int i = 0; i < lodGroupLODS.Length; i++)
  164. {
  165. MeshFilter lodMF = lodGroupLODS[i].renderers[0].GetComponent<MeshFilter>();
  166. if (lodMF)
  167. {
  168. lodMF.sharedMesh = Geometry.Create(splineContainer, ScaleData, OpacityData, generationSettings, meshFilter.transform.worldToLocalMatrix, lods[i].resolution);
  169. Debug.Log($"Creating LOD {i} at {lods[i].resolution}% for {lodMF.gameObject.name}");
  170. }
  171. }
  172. }
  173. */
  174. meshFilter.sharedMesh = Geometry.Create(splineContainer, settings, meshFilter.transform.worldToLocalMatrix, 100f, ScaleData, TransparencyData, FoamData);
  175. MeshRenderer meshRenderer = meshFilter.GetComponent<MeshRenderer>();
  176. if (meshRenderer)
  177. {
  178. meshRenderer.sortingOrder = orderInLayer;
  179. meshRenderer.shadowCastingMode = ShadowCastingMode.Off;
  180. if (settings.transparency.vertexColorChannel == Settings.VertexColorChannel.Green)
  181. {
  182. //If the material uses Stylized Water 2, enable vertex color transparency. Quickfix to avoid support overhead
  183. var vertexColorAlphaProp = Shader.PropertyToID("_VertexColorDepth");
  184. if (meshRenderer.sharedMaterial.HasProperty(vertexColorAlphaProp))
  185. {
  186. if (meshRenderer.sharedMaterial.GetFloat(vertexColorAlphaProp) == 0)
  187. {
  188. Debug.Log($"[River Modeler] A Stylized Water 2 material is in use, yet vertex color Transparency wasn't enabled on the material. Enabled now.", this.gameObject);
  189. meshRenderer.sharedMaterial.SetFloat(vertexColorAlphaProp, 1);
  190. #if UNITY_EDITOR
  191. UnityEditor.EditorUtility.SetDirty(meshRenderer.sharedMaterial);
  192. #endif
  193. }
  194. }
  195. //If the material uses Stylized Water 3, enable vertex color transparency. Quickfix to avoid support overhead
  196. vertexColorAlphaProp = Shader.PropertyToID("_VertexColorTransparency");
  197. if (meshRenderer.sharedMaterial.HasProperty(vertexColorAlphaProp))
  198. {
  199. if (meshRenderer.sharedMaterial.GetFloat(vertexColorAlphaProp) == 0)
  200. {
  201. Debug.Log($"[River Modeler] A Stylized Water 3 material is in use, yet vertex color Transparency wasn't enabled on the material. Enabled now.", this.gameObject);
  202. meshRenderer.sharedMaterial.SetFloat(vertexColorAlphaProp, 1);
  203. #if UNITY_EDITOR
  204. UnityEditor.EditorUtility.SetDirty(meshRenderer.sharedMaterial);
  205. #endif
  206. }
  207. }
  208. }
  209. if (settings.transparency.vertexColorChannel == Settings.VertexColorChannel.Alpha)
  210. {
  211. //If the material uses Stylized Water, enable vertex color foam. Quickfix to avoid support overhead
  212. var vertexColorFoamProp = Shader.PropertyToID("_VertexColorFoam");
  213. if (meshRenderer.sharedMaterial.HasProperty(vertexColorFoamProp))
  214. {
  215. if (meshRenderer.sharedMaterial.GetFloat(vertexColorFoamProp) == 0)
  216. {
  217. Debug.Log($"[River Modeler] A Stylized Water material is in use, yet vertex color Foam wasn't enabled on the material. Enabled now.", this.gameObject);
  218. meshRenderer.sharedMaterial.SetFloat(vertexColorFoamProp, 1);
  219. #if UNITY_EDITOR
  220. UnityEditor.EditorUtility.SetDirty(meshRenderer.sharedMaterial);
  221. #endif
  222. }
  223. }
  224. }
  225. }
  226. //Update mesh collider
  227. MeshCollider meshCollider = GetComponent<MeshCollider>();
  228. if (meshCollider) meshCollider.sharedMesh = meshFilter.sharedMesh;
  229. onPostRebuildRiver?.Invoke(this, updateFlags);
  230. onPostRebuild?.Invoke(updateFlags);
  231. #if MICROVERSE_SPLINES
  232. if (updateFlags.HasFlag(ChangeFlags.All) || updateFlags.HasFlag(ChangeFlags.Spline) || updateFlags.HasFlag(ChangeFlags.Geometry) || updateFlags.HasFlag(ChangeFlags.Data))
  233. {
  234. //MicroVerse syncing
  235. UpdateMicroVerseSpline();
  236. }
  237. #endif
  238. #endif
  239. }
  240. public partial void UpdateMicroVerseSpline();
  241. partial void DrawDebugging();
  242. private void OnDrawGizmosSelected()
  243. {
  244. DrawDebugging();
  245. #if SPLINES && MATHEMATICS
  246. //Moving the spline, then selecting the river again
  247. if (splineContainer && splineContainer.transform.hasChanged)
  248. {
  249. if (rebuildTriggers.HasFlag(RebuildTriggers.OnSplineTransformChange))
  250. {
  251. splineContainer.transform.hasChanged = false;
  252. Rebuild();
  253. }
  254. }
  255. #endif
  256. }
  257. }
  258. }