LightningMeshSurfaceScript.cs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. //
  2. // Procedural Lightning for Unity
  3. // (c) 2015 Digital Ruby, LLC
  4. // Source code may be used for personal or commercial projects.
  5. // Source code may NOT be redistributed or sold.
  6. //
  7. using UnityEngine;
  8. using System.Collections.Generic;
  9. namespace DigitalRuby.ThunderAndLightning
  10. {
  11. /// <summary>
  12. /// Script that generates lightning on the surface of a mesh
  13. /// </summary>
  14. public class LightningMeshSurfaceScript : LightningBoltPrefabScriptBase
  15. {
  16. /// <summary>The mesh filter. You must assign a mesh filter in order to create lightning on the mesh.</summary>
  17. [Header("Lightning Mesh Properties")]
  18. [Tooltip("The mesh filter. You must assign a mesh filter in order to create lightning on the mesh.")]
  19. public MeshFilter MeshFilter;
  20. /// <summary>Random range that the point will offset from the mesh, using the normal of the chosen point to offset</summary>
  21. [SingleLine("Random range that the point will offset from the mesh, using the normal of the chosen point to offset")]
  22. public RangeOfFloats MeshOffsetRange = new RangeOfFloats { Minimum = 0.5f, Maximum = 1.0f };
  23. /// <summary>Range for points in the lightning path</summary>
  24. [Header("Lightning Path Properties")]
  25. [SingleLine("Range for points in the lightning path")]
  26. public RangeOfIntegers PathLengthCount = new RangeOfIntegers { Minimum = 3, Maximum = 6 };
  27. /// <summary>Range for minimum distance between points in the lightning path</summary>
  28. [SingleLine("Range for minimum distance between points in the lightning path")]
  29. public RangeOfFloats MinimumPathDistanceRange = new RangeOfFloats { Minimum = 0.5f, Maximum = 1.0f };
  30. /// <summary>The maximum distance between mesh points. When walking the mesh, if a point is greater than this, the path direction is reversed. This tries to avoid paths crossing between mesh points that are not actually physically touching.</summary>
  31. [Tooltip("The maximum distance between mesh points. When walking the mesh, if a point is greater than this, the path direction is reversed. " +
  32. "This tries to avoid paths crossing between mesh points that are not actually physically touching.")]
  33. public float MaximumPathDistance = 2.0f;
  34. private float maximumPathDistanceSquared;
  35. /// <summary>Whether to use spline interpolation between the path points. Paths must be at least 4 points long to be splined.</summary>
  36. [Tooltip("Whether to use spline interpolation between the path points. Paths must be at least 4 points long to be splined.")]
  37. public bool Spline = false;
  38. /// <summary>For spline. the distance hint for each spline segment. Set to &lt;= 0 to use the generations to determine how many spline segments to use. If &gt; 0, it will be divided by Generations before being applied. This value is a guideline and is approximate, and not uniform on the spline.</summary>
  39. [Tooltip("For spline. the distance hint for each spline segment. Set to <= 0 to use the generations to determine how many spline segments to use. " +
  40. "If > 0, it will be divided by Generations before being applied. This value is a guideline and is approximate, and not uniform on the spline.")]
  41. public float DistancePerSegmentHint = 0.0f;
  42. private readonly List<Vector3> sourcePoints = new List<Vector3>();
  43. private Mesh previousMesh;
  44. private MeshHelper meshHelper;
  45. private void CheckMesh()
  46. {
  47. if (MeshFilter == null || MeshFilter.sharedMesh == null)
  48. {
  49. meshHelper = null;
  50. }
  51. else if (MeshFilter.sharedMesh != previousMesh)
  52. {
  53. if (previousMesh != null && !previousMesh.isReadable)
  54. {
  55. Debug.LogError("Mesh is not readable, cannot create lightning on mesh");
  56. }
  57. else
  58. {
  59. previousMesh = MeshFilter.sharedMesh;
  60. meshHelper = new MeshHelper(previousMesh);
  61. #if DEBUG
  62. if (previousMesh.GetTopology(0) != MeshTopology.Triangles)
  63. {
  64. Debug.LogError("Mesh topology must be triangles");
  65. }
  66. #endif
  67. }
  68. }
  69. }
  70. /// <summary>
  71. /// Create lightning bolt path parameters
  72. /// </summary>
  73. /// <returns>Lightning bolt path parameters</returns>
  74. protected override LightningBoltParameters OnCreateParameters()
  75. {
  76. LightningBoltParameters p = base.OnCreateParameters();
  77. p.Generator = LightningGeneratorPath.PathGeneratorInstance;
  78. return p;
  79. }
  80. /// <summary>
  81. /// Populate the points for a lightning path. This implementation simply picks a random point and then spreads out in random directions along the mesh.
  82. /// </summary>
  83. /// <param name="points">Points for the path to be filled in. Does not need to be cleared.</param>
  84. protected virtual void PopulateSourcePoints(List<Vector3> points)
  85. {
  86. if (meshHelper != null)
  87. {
  88. CreateRandomLightningPath(sourcePoints);
  89. }
  90. }
  91. /// <summary>
  92. /// Gets a path for lightning starting at a random point on the mesh
  93. /// </summary>
  94. /// <param name="points">Points list to receive points for the path</param>
  95. public void CreateRandomLightningPath(List<Vector3> points)
  96. {
  97. if (meshHelper == null)
  98. {
  99. return;
  100. }
  101. // we want a path of at least 2 triangles
  102. RaycastHit hit = new RaycastHit();
  103. int triangleIndex;
  104. maximumPathDistanceSquared = MaximumPathDistance * MaximumPathDistance;
  105. meshHelper.GenerateRandomPoint(ref hit, out triangleIndex);
  106. hit.distance = UnityEngine.Random.Range(MeshOffsetRange.Minimum, MeshOffsetRange.Maximum);
  107. Vector3 prevPoint = hit.point + (hit.normal * hit.distance);
  108. float pathDistanceSquared = UnityEngine.Random.Range(MinimumPathDistanceRange.Minimum, MinimumPathDistanceRange.Maximum);
  109. pathDistanceSquared *= pathDistanceSquared;
  110. sourcePoints.Add(MeshFilter.transform.TransformPoint(prevPoint));
  111. int dir = (UnityEngine.Random.Range(0, 1) == 1 ? 3 : -3);
  112. int pathLength = UnityEngine.Random.Range(PathLengthCount.Minimum, PathLengthCount.Maximum);
  113. while (pathLength > 0)
  114. {
  115. triangleIndex += dir;
  116. if (triangleIndex >= 0 && triangleIndex < meshHelper.Triangles.Length)
  117. {
  118. meshHelper.GetRaycastFromTriangleIndex(triangleIndex, ref hit);
  119. }
  120. else
  121. {
  122. dir = -dir;
  123. triangleIndex += dir;
  124. pathLength--;
  125. continue;
  126. }
  127. hit.distance = UnityEngine.Random.Range(MeshOffsetRange.Minimum, MeshOffsetRange.Maximum);
  128. Vector3 hitPoint = hit.point + (hit.normal * hit.distance);
  129. float distanceSquared = (hitPoint - prevPoint).sqrMagnitude;
  130. if (distanceSquared > maximumPathDistanceSquared)
  131. {
  132. break;
  133. }
  134. else if (distanceSquared >= pathDistanceSquared)
  135. {
  136. prevPoint = hitPoint;
  137. sourcePoints.Add(MeshFilter.transform.TransformPoint(hitPoint));
  138. pathLength--;
  139. pathDistanceSquared = UnityEngine.Random.Range(MinimumPathDistanceRange.Minimum, MinimumPathDistanceRange.Maximum);
  140. pathDistanceSquared *= pathDistanceSquared;
  141. }
  142. }
  143. }
  144. /// <summary>
  145. /// Start
  146. /// </summary>
  147. protected override void Start()
  148. {
  149. base.Start();
  150. }
  151. /// <summary>
  152. /// Update
  153. /// </summary>
  154. protected override void Update()
  155. {
  156. if (Time.timeScale > 0.0f)
  157. {
  158. CheckMesh();
  159. }
  160. base.Update();
  161. }
  162. /// <summary>
  163. /// Create a lightning bolt
  164. /// </summary>
  165. /// <param name="parameters">Parameters</param>
  166. public override void CreateLightningBolt(LightningBoltParameters parameters)
  167. {
  168. if (meshHelper == null)
  169. {
  170. return;
  171. }
  172. Generations = parameters.Generations = Mathf.Clamp(Generations, 1, LightningSplineScript.MaxSplineGenerations);
  173. sourcePoints.Clear();
  174. PopulateSourcePoints(sourcePoints);
  175. if (sourcePoints.Count > 1)
  176. {
  177. parameters.Points.Clear();
  178. if (Spline && sourcePoints.Count > 3)
  179. {
  180. LightningSplineScript.PopulateSpline(parameters.Points, sourcePoints, Generations, DistancePerSegmentHint, Camera);
  181. parameters.SmoothingFactor = (parameters.Points.Count - 1) / sourcePoints.Count;
  182. }
  183. else
  184. {
  185. parameters.Points.AddRange(sourcePoints);
  186. parameters.SmoothingFactor = 1;
  187. }
  188. base.CreateLightningBolt(parameters);
  189. }
  190. }
  191. }
  192. }