CascadeVFX.cs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  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.Collections.Generic;
  6. using sc.modeling.water.common.runtime;
  7. using UnityEngine;
  8. #if SPLINES
  9. using UnityEngine.Splines;
  10. using Interpolators = UnityEngine.Splines.Interpolators;
  11. #endif
  12. #if MATHEMATICS
  13. using Unity.Mathematics;
  14. #endif
  15. namespace sc.modeling.river.runtime
  16. {
  17. [AddComponentMenu("Water/River/River Cascade VFX")]
  18. [HelpURL("https://staggart.xyz/river-modeler-docs/?section=cascade-vfx")]
  19. public class CascadeVFX : RiverVFX
  20. {
  21. [Tooltip("Consider a point of the river as being part of a cascade if its height dropped low enough, compared to a point before it.")]
  22. [Min(0.05f)]
  23. public float heightDropThreshold = 0.1f;
  24. [Tooltip("Shift the particle back/forward along the spline")]
  25. public float shifting = 0f;
  26. [Range(0f, 1f)]
  27. public float randomOffset = 1f;
  28. [Tooltip("Minimum particle distance over the width of the river. Lower values provide better coverage, but a particle cloud too dense will impact performance negatively")]
  29. [Min(0.05f)]
  30. public float minParticleDistance = 0.5f;
  31. public float margin;
  32. public override void GenerateEmitters()
  33. {
  34. #if VFX_GRAPH && MATHEMATICS && SPLINES
  35. if (!river || !targetEffect) return;
  36. var splineContainer = river.splineContainer;
  37. Interpolators.LerpFloat3 scaleInterpolator = new Interpolators.LerpFloat3();
  38. emitters = new List<VFX.Emitter>();
  39. Vector3 min = Vector3.one * float.MaxValue;
  40. Vector3 max = Vector3.one * float.MinValue;
  41. if (confines == Confines.Bounds) bounds = new Bounds(this.transform.position, boundsSize);
  42. for (int s = 0; s < splineContainer.Splines.Count; s++)
  43. {
  44. float length = splineContainer.Splines[s].CalculateLength(splineContainer.transform.localToWorldMatrix);
  45. int sampleCount = Mathf.RoundToInt(length / 0.25f);
  46. float stride = 1f / sampleCount;
  47. for (int y = 0; y <= sampleCount; y++)
  48. {
  49. float t = (float)y / (float)(sampleCount); //Normalized position
  50. if(t < stride) continue;
  51. splineContainer.Splines[s].Evaluate(t, out var position, out var tangent, out var normal);
  52. //position = splineContainer.transform.TransformPoint(position);
  53. if (confines == Confines.Bounds)
  54. {
  55. //Early out if spline point falls outside of bounds
  56. if (bounds.Contains(splineContainer.transform.TransformPoint(position)) == false) continue;
  57. }
  58. //Analyze the next point to check if there is a large enough drop
  59. splineContainer.Splines[s].Evaluate(t + stride, out var nextPosition, out _, out _);
  60. //nextPosition = splineContainer.transform.TransformPoint(nextPosition);
  61. float delta = position.y - nextPosition.y;
  62. if (delta >= heightDropThreshold)
  63. {
  64. float distance = t * length;
  65. float3 scale = river.ScaleData[s].Evaluate(splineContainer.Splines[s], distance, river.ScaleData[s].PathIndexUnit, scaleInterpolator);
  66. //bi-tangent
  67. float3 right = math.normalize(math.cross(math.normalize(tangent), normal));
  68. float riverWidth = river.settings.shape.width * scale.x;
  69. //Distance between emitters along the width of the edge
  70. float minDistance = minParticleDistance;
  71. int widthSegments = Mathf.CeilToInt(riverWidth / (minDistance));
  72. //float margin = minDistance;
  73. for (int x = 0; x < widthSegments; x++)
  74. {
  75. float xt = (float)x / (float)widthSegments;
  76. float width = xt * riverWidth;
  77. if(width < margin || width > (riverWidth - margin)) continue;
  78. float3 newPos;
  79. float shift = shifting / length; //Shifting value from meters to t
  80. //New sampling position (shifted up/down the spline randomly)
  81. float t2 = t + shift + (stride * UnityEngine.Random.value * randomOffset);
  82. splineContainer.Splines[s].Evaluate(t2, out newPos, out _, out _);
  83. newPos = splineContainer.transform.TransformPoint(newPos);
  84. float cascadeWidth = ((float)x * minDistance) - margin;
  85. float3 spawnPoint =
  86. newPos
  87. - (right * (riverWidth * 0.5f)) //Offset to left by half
  88. //+ (right * margin) //Offset forward by margin
  89. + (right * cascadeWidth); //Relative offset over width
  90. if (confines == Confines.Bounds)
  91. {
  92. if (bounds.Contains(spawnPoint) == false) continue;
  93. }
  94. //To effect's local space
  95. spawnPoint = targetEffect.transform.InverseTransformPoint(spawnPoint);
  96. min = Vector3.Min(spawnPoint, min);
  97. max = Vector3.Max(spawnPoint, max);
  98. VFX.Emitter emitter = new VFX.Emitter
  99. {
  100. position = spawnPoint,
  101. //Spline local to world
  102. velocity = splineContainer.transform.TransformDirection(tangent)
  103. };
  104. //World to local of effect
  105. emitter.velocity = targetEffect.transform.InverseTransformDirection(emitter.velocity);
  106. emitters.Add(emitter);
  107. }
  108. }
  109. }
  110. }
  111. particleBounds = new Bounds();
  112. particleBounds.SetMinMax(min, max);
  113. ApplyEmitters();
  114. #endif
  115. }
  116. }
  117. }