LightningBoltDataStructures.cs 89 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561
  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. // uncomment to enable profiling using stopwatch and debug.log
  8. // #define ENABLE_PROFILING
  9. #if NETFX_CORE
  10. #define TASK_AVAILABLE
  11. using System.Threading.Tasks;
  12. #endif
  13. using UnityEngine;
  14. using System;
  15. using System.Collections;
  16. using System.Collections.Generic;
  17. using System.Threading;
  18. using System.Collections.Concurrent;
  19. namespace DigitalRuby.ThunderAndLightning
  20. {
  21. /// <summary>
  22. /// Quality settings for lightning
  23. /// </summary>
  24. public enum LightningBoltQualitySetting
  25. {
  26. /// <summary>
  27. /// Use all settings from the script, ignoring the global quality setting
  28. /// </summary>
  29. UseScript,
  30. /// <summary>
  31. /// Use the global quality setting to determine lightning quality and maximum number of lights and shadowing
  32. /// </summary>
  33. LimitToQualitySetting
  34. }
  35. /// <summary>
  36. /// Camera modes
  37. /// </summary>
  38. public enum CameraMode
  39. {
  40. /// <summary>
  41. /// Auto detect
  42. /// </summary>
  43. Auto,
  44. /// <summary>
  45. /// Force perspective camera lightning
  46. /// </summary>
  47. Perspective,
  48. /// <summary>
  49. /// Force orthographic XY lightning
  50. /// </summary>
  51. OrthographicXY,
  52. /// <summary>
  53. /// Force orthographic XZ lightning
  54. /// </summary>
  55. OrthographicXZ,
  56. /// <summary>
  57. /// Unknown camera mode (do not use)
  58. /// </summary>
  59. Unknown
  60. }
  61. /// <summary>
  62. /// Lightning custom transfrom state
  63. /// </summary>
  64. public enum LightningCustomTransformState
  65. {
  66. /// <summary>
  67. /// Started
  68. /// </summary>
  69. Started,
  70. /// <summary>
  71. /// Executing
  72. /// </summary>
  73. Executing,
  74. /// <summary>
  75. /// Ended
  76. /// </summary>
  77. Ended
  78. }
  79. /// <summary>
  80. /// Lightning custom transform info
  81. /// </summary>
  82. public class LightningCustomTransformStateInfo
  83. {
  84. /// <summary>
  85. /// State
  86. /// </summary>
  87. public LightningCustomTransformState State { get; set; }
  88. /// <summary>
  89. /// Parameters
  90. /// </summary>
  91. public LightningBoltParameters Parameters { get; set; }
  92. /// <summary>
  93. /// Lightning bolt start position
  94. /// </summary>
  95. public Vector3 BoltStartPosition;
  96. /// <summary>
  97. /// Lightning bolt end position
  98. /// </summary>
  99. public Vector3 BoltEndPosition;
  100. /// <summary>
  101. /// Transform
  102. /// </summary>
  103. public Transform Transform;
  104. /// <summary>
  105. /// Start position transform
  106. /// </summary>
  107. public Transform StartTransform;
  108. /// <summary>
  109. /// End position transform
  110. /// </summary>
  111. public Transform EndTransform;
  112. /// <summary>
  113. /// User defined object
  114. /// </summary>
  115. public object UserInfo;
  116. private static readonly List<LightningCustomTransformStateInfo> cache = new List<LightningCustomTransformStateInfo>();
  117. /// <summary>
  118. /// Get or create lightning custom transform state info from cache
  119. /// </summary>
  120. /// <returns>LightningCustomTransformStateInfo</returns>
  121. public static LightningCustomTransformStateInfo GetOrCreateStateInfo()
  122. {
  123. if (cache.Count == 0)
  124. {
  125. return new LightningCustomTransformStateInfo();
  126. }
  127. int idx = cache.Count - 1;
  128. LightningCustomTransformStateInfo result = cache[idx];
  129. cache.RemoveAt(idx);
  130. return result;
  131. }
  132. /// <summary>
  133. /// Put LightningCustomTransformStateInfo back into the cache
  134. /// </summary>
  135. /// <param name="info">LightningCustomTransformStateInfo to return to cache</param>
  136. public static void ReturnStateInfoToCache(LightningCustomTransformStateInfo info)
  137. {
  138. if (info != null)
  139. {
  140. info.Transform = info.StartTransform = info.EndTransform = null;
  141. info.UserInfo = null;
  142. cache.Add(info);
  143. }
  144. }
  145. }
  146. /// <summary>
  147. /// Lightning custom transform delegate
  148. /// </summary>
  149. [System.Serializable]
  150. public class LightningCustomTransformDelegate : UnityEngine.Events.UnityEvent<LightningCustomTransformStateInfo> { }
  151. /// <summary>
  152. /// Lightning light parameters
  153. /// </summary>
  154. [System.Serializable]
  155. public class LightningLightParameters
  156. {
  157. /// <summary>
  158. /// Light render mode
  159. /// </summary>
  160. [Tooltip("Light render mode - leave as auto unless you have special use cases")]
  161. [HideInInspector]
  162. public LightRenderMode RenderMode = LightRenderMode.Auto;
  163. /// <summary>
  164. /// Color of light
  165. /// </summary>
  166. [Tooltip("Color of the light")]
  167. public Color LightColor = Color.white;
  168. /// <summary>
  169. /// What percent of segments should have a light? Keep this pretty low for performance, i.e. 0.05 or lower depending on generations
  170. /// Set really really low to only have 1 light, i.e. 0.0000001f
  171. /// For example, at generations 5, the main trunk has 32 segments, 64 at generation 6, etc.
  172. /// If non-zero, there wil be at least one light in the middle
  173. /// </summary>
  174. [Tooltip("What percent of segments should have a light? For performance you may want to keep this small.")]
  175. [Range(0.0f, 1.0f)]
  176. public float LightPercent = 0.000001f;
  177. /// <summary>
  178. /// What percent of lights created should cast shadows?
  179. /// </summary>
  180. [Tooltip("What percent of lights created should cast shadows?")]
  181. [Range(0.0f, 1.0f)]
  182. public float LightShadowPercent;
  183. /// <summary>
  184. /// Light intensity
  185. /// </summary>
  186. [Tooltip("Light intensity")]
  187. [Range(0.0f, 8.0f)]
  188. public float LightIntensity = 0.5f;
  189. /// <summary>
  190. /// Light multiplier. Can set to a high number (millions) if HDRP (lumens) support is needed.
  191. /// </summary>
  192. [Tooltip("Light multiplier. Can set to a high number (millions) if HDRP (lumens) support is needed.")]
  193. [Range(0.0f, 10000000.0f)]
  194. public float LightMultiplier = 1.0f;
  195. /// <summary>
  196. /// Bounce intensity
  197. /// </summary>
  198. [Tooltip("Bounce intensity")]
  199. [Range(0.0f, 8.0f)]
  200. public float BounceIntensity;
  201. /// <summary>
  202. /// Shadow strength, 0 - 1. 0 means all light, 1 means all shadow
  203. /// </summary>
  204. [Tooltip("Shadow strength, 0 means all light, 1 means all shadow")]
  205. [Range(0.0f, 1.0f)]
  206. public float ShadowStrength = 1.0f;
  207. /// <summary>
  208. /// Shadow bias
  209. /// </summary>
  210. [Tooltip("Shadow bias, 0 - 2")]
  211. [Range(0.0f, 2.0f)]
  212. public float ShadowBias = 0.05f;
  213. /// <summary>
  214. /// Shadow normal bias
  215. /// </summary>
  216. [Tooltip("Shadow normal bias, 0 - 3")]
  217. [Range(0.0f, 3.0f)]
  218. public float ShadowNormalBias = 0.4f;
  219. /// <summary>
  220. /// Light range
  221. /// </summary>
  222. [Tooltip("The range of each light created")]
  223. public float LightRange;
  224. /// <summary>
  225. /// Only light up objects that match this layer mask
  226. /// </summary>
  227. [Tooltip("Only light objects that match this layer mask")]
  228. public LayerMask CullingMask = ~0;
  229. /// <summary>Offset from camera position when in orthographic mode</summary>
  230. [Tooltip("Offset from camera position when in orthographic mode")]
  231. [Range(-1000.0f, 1000.0f)]
  232. public float OrthographicOffset = 0.0f;
  233. /// <summary>Increase the duration of light fade in compared to the lightning fade.</summary>
  234. [Tooltip("Increase the duration of light fade in compared to the lightning fade.")]
  235. [Range(0.0f, 20.0f)]
  236. public float FadeInMultiplier = 1.0f;
  237. /// <summary>Increase the duration of light fully lit compared to the lightning fade.</summary>
  238. [Tooltip("Increase the duration of light fully lit compared to the lightning fade.")]
  239. [Range(0.0f, 20.0f)]
  240. public float FadeFullyLitMultiplier = 1.0f;
  241. /// <summary>Increase the duration of light fade out compared to the lightning fade.</summary>
  242. [Tooltip("Increase the duration of light fade out compared to the lightning fade.")]
  243. [Range(0.0f, 20.0f)]
  244. public float FadeOutMultiplier = 1.0f;
  245. /// <summary>
  246. /// Should light be shown for these parameters?
  247. /// </summary>
  248. public bool HasLight
  249. {
  250. get { return (LightColor.a > 0.0f && LightIntensity >= 0.01f && LightPercent >= 0.0000001f && LightRange > 0.01f); }
  251. }
  252. }
  253. /// <summary>
  254. /// Parameters that control lightning bolt behavior
  255. /// </summary>
  256. [System.Serializable]
  257. public sealed class LightningBoltParameters
  258. {
  259. #region Internal use only
  260. // INTERNAL USE ONLY!!!
  261. private static int randomSeed = Environment.TickCount;
  262. private static readonly List<LightningBoltParameters> cache = new List<LightningBoltParameters>();
  263. internal int generationWhereForksStop;
  264. internal int forkednessCalculated;
  265. internal LightningBoltQualitySetting quality;
  266. internal float delaySeconds;
  267. internal int maxLights;
  268. // END INTERNAL USE ONLY
  269. #endregion Internal use only
  270. /// <summary>
  271. /// Scale all scalar parameters by this value (i.e. trunk width, turbulence, turbulence velocity)
  272. /// </summary>
  273. public static float Scale = 1.0f;
  274. /// <summary>
  275. /// Contains quality settings for different quality levels. By default, this assumes 6 quality levels, so if you have your own
  276. /// custom quality setting levels, you may want to clear this dictionary out and re-populate it with your own limits
  277. /// </summary>
  278. public static readonly Dictionary<int, LightningQualityMaximum> QualityMaximums = new Dictionary<int, LightningQualityMaximum>();
  279. static LightningBoltParameters()
  280. {
  281. string[] names = QualitySettings.names;
  282. for (int i = 0; i < names.Length; i++)
  283. {
  284. switch (i)
  285. {
  286. case 0:
  287. QualityMaximums[i] = new LightningQualityMaximum { MaximumGenerations = 3, MaximumLightPercent = 0, MaximumShadowPercent = 0.0f };
  288. break;
  289. case 1:
  290. QualityMaximums[i] = new LightningQualityMaximum { MaximumGenerations = 4, MaximumLightPercent = 0, MaximumShadowPercent = 0.0f };
  291. break;
  292. case 2:
  293. QualityMaximums[i] = new LightningQualityMaximum { MaximumGenerations = 5, MaximumLightPercent = 0.1f, MaximumShadowPercent = 0.0f };
  294. break;
  295. case 3:
  296. QualityMaximums[i] = new LightningQualityMaximum { MaximumGenerations = 5, MaximumLightPercent = 0.1f, MaximumShadowPercent = 0.0f };
  297. break;
  298. case 4:
  299. QualityMaximums[i] = new LightningQualityMaximum { MaximumGenerations = 6, MaximumLightPercent = 0.05f, MaximumShadowPercent = 0.1f };
  300. break;
  301. case 5:
  302. QualityMaximums[i] = new LightningQualityMaximum { MaximumGenerations = 7, MaximumLightPercent = 0.025f, MaximumShadowPercent = 0.05f };
  303. break;
  304. default:
  305. QualityMaximums[i] = new LightningQualityMaximum { MaximumGenerations = 8, MaximumLightPercent = 0.025f, MaximumShadowPercent = 0.05f };
  306. break;
  307. }
  308. }
  309. }
  310. /// <summary>
  311. /// Constructor
  312. /// </summary>
  313. public LightningBoltParameters()
  314. {
  315. unchecked
  316. {
  317. random = currentRandom = new System.Random(randomSeed++);
  318. }
  319. Points = new List<Vector3>();
  320. }
  321. /// <summary>
  322. /// Generator to create the lightning bolt from the parameters
  323. /// </summary>
  324. public LightningGenerator Generator;
  325. /// <summary>
  326. /// Start of the bolt
  327. /// </summary>
  328. public Vector3 Start;
  329. /// <summary>
  330. /// End of the bolt
  331. /// </summary>
  332. public Vector3 End;
  333. /// <summary>
  334. /// X, Y and Z radius variance from Start
  335. /// </summary>
  336. public Vector3 StartVariance;
  337. /// <summary>
  338. /// X, Y and Z radius variance from End
  339. /// </summary>
  340. public Vector3 EndVariance;
  341. /// <summary>
  342. /// Custom transform action, null if none
  343. /// </summary>
  344. public System.Action<LightningCustomTransformStateInfo> CustomTransform;
  345. private int generations;
  346. /// <summary>
  347. /// Number of generations (0 for just a point light, otherwise 1 - 8). Higher generations have lightning with finer detail but more expensive to create.
  348. /// </summary>
  349. public int Generations
  350. {
  351. get { return generations; }
  352. set
  353. {
  354. int v = Mathf.Clamp(value, 1, 8);
  355. if (quality == LightningBoltQualitySetting.UseScript)
  356. {
  357. generations = v;
  358. }
  359. else
  360. {
  361. LightningQualityMaximum maximum;
  362. int level = QualitySettings.GetQualityLevel();
  363. if (QualityMaximums.TryGetValue(level, out maximum))
  364. {
  365. generations = Mathf.Min(maximum.MaximumGenerations, v);
  366. }
  367. else
  368. {
  369. generations = v;
  370. Debug.LogError("Unable to read lightning quality settings from level " + level.ToString());
  371. }
  372. }
  373. }
  374. }
  375. /// <summary>
  376. /// How long the bolt should live in seconds
  377. /// </summary>
  378. public float LifeTime;
  379. /// <summary>
  380. /// Minimum delay
  381. /// </summary>
  382. public float Delay;
  383. /// <summary>
  384. /// How long to wait in seconds before starting additional lightning bolts
  385. /// </summary>
  386. public RangeOfFloats DelayRange;
  387. /// <summary>
  388. /// How chaotic is the main trunk of lightning? (0 - 1). Higher numbers create more chaotic lightning.
  389. /// </summary>
  390. public float ChaosFactor;
  391. /// <summary>
  392. /// How chaotic are the forks of the lightning? (0 - 1). Higher numbers create more chaotic lightning.
  393. /// </summary>
  394. public float ChaosFactorForks = -1.0f;
  395. /// <summary>
  396. /// The width of the trunk
  397. /// </summary>
  398. public float TrunkWidth;
  399. /// <summary>
  400. /// The ending width of a segment of lightning
  401. /// </summary>
  402. public float EndWidthMultiplier = 0.5f;
  403. /// <summary>
  404. /// Intensity of the lightning
  405. /// </summary>
  406. public float Intensity = 1.0f;
  407. /// <summary>
  408. /// Intensity of the glow
  409. /// </summary>
  410. public float GlowIntensity;
  411. /// <summary>
  412. /// Glow width multiplier
  413. /// </summary>
  414. public float GlowWidthMultiplier;
  415. /// <summary>
  416. /// How forked the lightning should be, 0 for none, 1 for LOTS of forks
  417. /// </summary>
  418. public float Forkedness;
  419. /// <summary>
  420. /// This is subtracted from the initial generations value, and any generation below that cannot have a fork
  421. /// </summary>
  422. public int GenerationWhereForksStopSubtractor = 5;
  423. /// <summary>
  424. /// Tint color for the lightning, this is applied to both the lightning and the glow. Unlike the script properties for coloring which
  425. /// are applied per material, this is applied at the mesh level and as such different bolts on the same script can use different color values.
  426. /// </summary>
  427. public Color32 Color = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue);
  428. /// <summary>
  429. /// Tint color for main trunk of lightning
  430. /// </summary>
  431. public Color32 MainTrunkTintColor = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue);
  432. /// <summary>
  433. /// Used to generate random numbers. Not thread safe.
  434. /// </summary>
  435. public System.Random Random
  436. {
  437. get { return currentRandom; }
  438. set
  439. {
  440. random = value ?? random;
  441. currentRandom = (randomOverride ?? random);
  442. }
  443. }
  444. private System.Random random;
  445. private System.Random currentRandom;
  446. /// <summary>
  447. /// Override Random to a different Random. This gets set back to null when the parameters go back to the cache. Great for a one time bolt that looks a certain way.
  448. /// </summary>
  449. public System.Random RandomOverride
  450. {
  451. get { return randomOverride; }
  452. set
  453. {
  454. randomOverride = value;
  455. currentRandom = (randomOverride ?? random);
  456. }
  457. }
  458. private System.Random randomOverride;
  459. /// <summary>
  460. /// The percent of time the lightning should fade in and out (0 - 1). Example: 0.2 would fade in for 20% of the lifetime and fade out for 20% of the lifetime. Set to 0 for no fade.
  461. /// </summary>
  462. public float FadePercent = 0.15f;
  463. /// <summary>
  464. /// Modify the fade in time for FadePercent (0 - 1)
  465. /// </summary>
  466. public float FadeInMultiplier = 1.0f;
  467. /// <summary>
  468. /// Modify the fully lit time for FadePercent (0 - 1)
  469. /// </summary>
  470. public float FadeFullyLitMultiplier = 1.0f;
  471. /// <summary>
  472. /// Modify the fade out time for FadePercent (0 - 1)
  473. /// </summary>
  474. public float FadeOutMultiplier = 1.0f;
  475. private float growthMultiplier;
  476. /// <summary>
  477. /// A value between 0 and 0.999 that determines how fast the lightning should grow over the lifetime. A value of 1 grows slowest, 0 grows instantly
  478. /// </summary>
  479. public float GrowthMultiplier
  480. {
  481. get { return growthMultiplier; }
  482. set { growthMultiplier = Mathf.Clamp(value, 0.0f, 0.999f); }
  483. }
  484. /// <summary>
  485. /// Minimum distance multiplier for forks
  486. /// </summary>
  487. public float ForkLengthMultiplier = 0.6f;
  488. /// <summary>
  489. /// Variance of the fork distance (random range of 0 to n is added to ForkLengthMultiplier)
  490. /// </summary>
  491. public float ForkLengthVariance = 0.2f;
  492. /// <summary>
  493. /// Forks will have their end widths multiplied by this value
  494. /// </summary>
  495. public float ForkEndWidthMultiplier = 1.0f;
  496. /// <summary>
  497. /// Light parameters, null for none
  498. /// </summary>
  499. public LightningLightParameters LightParameters;
  500. /// <summary>
  501. /// Points for the trunk to follow - not all generators support this
  502. /// </summary>
  503. public List<Vector3> Points { get; set; }
  504. /// <summary>
  505. /// The amount of smoothing applied. For example, if there were 4 original points and smoothing / spline created 32 points, this value would be 8 - not all generators support this
  506. /// </summary>
  507. public int SmoothingFactor;
  508. /// <summary>
  509. /// Get a multiplier for fork distance
  510. /// </summary>
  511. /// <returns>Fork multiplier</returns>
  512. public float ForkMultiplier()
  513. {
  514. return ((float)Random.NextDouble() * ForkLengthVariance) + ForkLengthMultiplier;
  515. }
  516. /// <summary>
  517. /// Apply variance to a vector
  518. /// </summary>
  519. /// <param name="pos">Position</param>
  520. /// <param name="variance">Variance</param>
  521. /// <returns>New position</returns>
  522. public Vector3 ApplyVariance(Vector3 pos, Vector3 variance)
  523. {
  524. return new Vector3
  525. (
  526. pos.x + (((float)Random.NextDouble() * 2.0f) - 1.0f) * variance.x,
  527. pos.y + (((float)Random.NextDouble() * 2.0f) - 1.0f) * variance.y,
  528. pos.z + (((float)Random.NextDouble() * 2.0f) - 1.0f) * variance.z
  529. );
  530. }
  531. /// <summary>
  532. /// Reset parameters
  533. /// </summary>
  534. public void Reset()
  535. {
  536. Start = End = Vector3.zero;
  537. Generator = null;
  538. SmoothingFactor = 0;
  539. RandomOverride = null;
  540. CustomTransform = null;
  541. if (Points != null)
  542. {
  543. Points.Clear();
  544. }
  545. }
  546. /// <summary>
  547. /// Get or create lightning bolt parameters. If cache has parameters, one is taken, otherwise a new object is created. NOT thread safe.
  548. /// </summary>
  549. /// <returns>Lightning bolt parameters</returns>
  550. public static LightningBoltParameters GetOrCreateParameters()
  551. {
  552. LightningBoltParameters p;
  553. if (cache.Count == 0)
  554. {
  555. unchecked
  556. {
  557. p = new LightningBoltParameters();
  558. }
  559. }
  560. else
  561. {
  562. int i = cache.Count - 1;
  563. p = cache[i];
  564. cache.RemoveAt(i);
  565. }
  566. return p;
  567. }
  568. /// <summary>
  569. /// Return parameters to cache. NOT thread safe.
  570. /// </summary>
  571. /// <param name="p">Parameters</param>
  572. public static void ReturnParametersToCache(LightningBoltParameters p)
  573. {
  574. if (!cache.Contains(p))
  575. {
  576. // reset variables that are state-machine dependant
  577. p.Reset();
  578. cache.Add(p);
  579. }
  580. }
  581. }
  582. /// <summary>
  583. /// A group of lightning bolt segments, such as the main trunk of the lightning bolt
  584. /// </summary>
  585. public class LightningBoltSegmentGroup
  586. {
  587. /// <summary>
  588. /// Width
  589. /// </summary>
  590. public float LineWidth;
  591. /// <summary>
  592. /// Start index of the segment to render (for performance, some segments are not rendered and only used for calculations)
  593. /// </summary>
  594. public int StartIndex;
  595. /// <summary>
  596. /// Generation
  597. /// </summary>
  598. public int Generation;
  599. /// <summary>
  600. /// Delay before rendering should start
  601. /// </summary>
  602. public float Delay;
  603. /// <summary>
  604. /// Peak start, the segments should be fully visible at this point
  605. /// </summary>
  606. public float PeakStart;
  607. /// <summary>
  608. /// Peak end, the segments should start to go away after this point
  609. /// </summary>
  610. public float PeakEnd;
  611. /// <summary>
  612. /// Total life time the group will be alive in seconds
  613. /// </summary>
  614. public float LifeTime;
  615. /// <summary>
  616. /// The width can be scaled down to the last segment by this amount if desired
  617. /// </summary>
  618. public float EndWidthMultiplier;
  619. /// <summary>
  620. /// Color for the group
  621. /// </summary>
  622. public Color32 Color;
  623. /// <summary>
  624. /// Total number of active segments
  625. /// </summary>
  626. public int SegmentCount { get { return Segments.Count - StartIndex; } }
  627. /// <summary>
  628. /// Segments
  629. /// </summary>
  630. public readonly List<LightningBoltSegment> Segments = new List<LightningBoltSegment>();
  631. /// <summary>
  632. /// Lights
  633. /// </summary>
  634. public readonly List<Light> Lights = new List<Light>();
  635. /// <summary>
  636. /// Light parameters
  637. /// </summary>
  638. public LightningLightParameters LightParameters;
  639. /// <summary>
  640. /// Return the group to its cache if there is one
  641. /// </summary>
  642. public void Reset()
  643. {
  644. LightParameters = null;
  645. Segments.Clear();
  646. Lights.Clear();
  647. StartIndex = 0;
  648. }
  649. }
  650. /// <summary>
  651. /// A single segment of a lightning bolt
  652. /// </summary>
  653. public struct LightningBoltSegment
  654. {
  655. /// <summary>
  656. /// Segment start
  657. /// </summary>
  658. public Vector3 Start;
  659. /// <summary>
  660. /// Segment end
  661. /// </summary>
  662. public Vector3 End;
  663. /// <summary>
  664. /// ToString
  665. /// </summary>
  666. /// <returns>String</returns>
  667. public override string ToString()
  668. {
  669. return Start.ToString() + ", " + End.ToString();
  670. }
  671. }
  672. /// <summary>
  673. /// Contains maximum values for a given quality settings
  674. /// </summary>
  675. public class LightningQualityMaximum
  676. {
  677. /// <summary>
  678. /// Maximum generations
  679. /// </summary>
  680. public int MaximumGenerations { get; set; }
  681. /// <summary>
  682. /// Maximum light percent
  683. /// </summary>
  684. public float MaximumLightPercent { get; set; }
  685. /// <summary>
  686. /// Maximum light shadow percent
  687. /// </summary>
  688. public float MaximumShadowPercent { get; set; }
  689. }
  690. /// <summary>
  691. /// Lightning bolt dependencies
  692. /// </summary>
  693. public class LightningBoltDependencies
  694. {
  695. /// <summary>
  696. /// Parent - do not access from threads
  697. /// </summary>
  698. public GameObject Parent;
  699. /// <summary>
  700. /// Material for glow - do not access from threads
  701. /// </summary>
  702. public Material LightningMaterialMesh;
  703. /// <summary>
  704. /// Material for bolt - do not access from threads
  705. /// </summary>
  706. public Material LightningMaterialMeshNoGlow;
  707. /// <summary>
  708. /// Origin particle system - do not access from threads
  709. /// </summary>
  710. public ParticleSystem OriginParticleSystem;
  711. /// <summary>
  712. /// Dest particle system - do not access from threads
  713. /// </summary>
  714. public ParticleSystem DestParticleSystem;
  715. /// <summary>
  716. /// Camera position
  717. /// </summary>
  718. public Vector3 CameraPos;
  719. /// <summary>
  720. /// Is camera 2D?
  721. /// </summary>
  722. public bool CameraIsOrthographic;
  723. /// <summary>
  724. /// Camera mode
  725. /// </summary>
  726. public CameraMode CameraMode;
  727. /// <summary>
  728. /// Use world space
  729. /// </summary>
  730. public bool UseWorldSpace;
  731. /// <summary>
  732. /// Level of detail distance
  733. /// </summary>
  734. public float LevelOfDetailDistance;
  735. /// <summary>
  736. /// Sort layer name
  737. /// </summary>
  738. public string SortLayerName;
  739. /// <summary>
  740. /// Order in layer
  741. /// </summary>
  742. public int SortOrderInLayer;
  743. /// <summary>
  744. /// Parameters
  745. /// </summary>
  746. public ICollection<LightningBoltParameters> Parameters;
  747. /// <summary>
  748. /// Thread state
  749. /// </summary>
  750. public LightningThreadState ThreadState;
  751. /// <summary>
  752. /// Method to start co-routines
  753. /// </summary>
  754. public Func<IEnumerator, Coroutine> StartCoroutine;
  755. /// <summary>
  756. /// Call this when a light is added
  757. /// </summary>
  758. public Action<Light> LightAdded;
  759. /// <summary>
  760. /// Call this when a light is removed
  761. /// </summary>
  762. public Action<Light> LightRemoved;
  763. /// <summary>
  764. /// Call this when the bolt becomes active
  765. /// </summary>
  766. public Action<LightningBolt> AddActiveBolt;
  767. /// <summary>
  768. /// Returns the dependencies to their cache
  769. /// </summary>
  770. public Action<LightningBoltDependencies> ReturnToCache;
  771. /// <summary>
  772. /// Runs when a lightning bolt is started (parameters, start, end)
  773. /// </summary>
  774. public Action<LightningBoltParameters, Vector3, Vector3> LightningBoltStarted;
  775. /// <summary>
  776. /// Runs when a lightning bolt is ended (parameters, start, end)
  777. /// </summary>
  778. public Action<LightningBoltParameters, Vector3, Vector3> LightningBoltEnded;
  779. }
  780. /// <summary>
  781. /// Lightning bolt
  782. /// </summary>
  783. public class LightningBolt
  784. {
  785. #region LineRendererMesh
  786. /// <summary>
  787. /// Class the encapsulates a game object, and renderer for lightning bolt meshes
  788. /// </summary>
  789. public class LineRendererMesh
  790. {
  791. #region Public variables
  792. /// <summary>
  793. /// Game object
  794. /// </summary>
  795. public GameObject GameObject { get; private set; }
  796. /// <summary>
  797. /// Material for glow
  798. /// </summary>
  799. public Material MaterialGlow
  800. {
  801. get { return meshRendererGlow.sharedMaterial; }
  802. set { meshRendererGlow.sharedMaterial = value; }
  803. }
  804. /// <summary>
  805. /// Material for bolt
  806. /// </summary>
  807. public Material MaterialBolt
  808. {
  809. get { return meshRendererBolt.sharedMaterial; }
  810. set { meshRendererBolt.sharedMaterial = value; }
  811. }
  812. /// <summary>
  813. /// Mesh renderer for glow
  814. /// </summary>
  815. public MeshRenderer MeshRendererGlow
  816. {
  817. get { return meshRendererGlow; }
  818. }
  819. /// <summary>
  820. /// Mesh renderer for bolt
  821. /// </summary>
  822. public MeshRenderer MeshRendererBolt
  823. {
  824. get { return meshRendererBolt; }
  825. }
  826. /// <summary>
  827. /// User defined int
  828. /// </summary>
  829. public int Tag { get; set; }
  830. #endregion Public variables
  831. #region Public properties
  832. /// <summary>
  833. /// Custom transform, null if none
  834. /// </summary>
  835. public System.Action<LightningCustomTransformStateInfo> CustomTransform { get; set; }
  836. /// <summary>
  837. /// The transform component
  838. /// </summary>
  839. public Transform Transform { get; private set; }
  840. /// <summary>
  841. /// Is the line renderer empty?
  842. /// </summary>
  843. public bool Empty { get { return vertices.Count == 0; } }
  844. #endregion Public properties
  845. #region Public methods
  846. /// <summary>
  847. /// Constructor
  848. /// </summary>
  849. /// <param name="dependencies">Dependencies</param>
  850. public LineRendererMesh(LightningBoltDependencies dependencies)
  851. {
  852. dependencies.ThreadState.AddActionForMainThread(b =>
  853. {
  854. GameObject = new GameObject("LightningBoltMeshRenderer");
  855. GameObject.SetActive(false); // call Begin to activate
  856. mesh = new Mesh { name = "ProceduralLightningMesh" };
  857. mesh.MarkDynamic();
  858. GameObject glowObject = new GameObject("LightningBoltMeshRendererGlow");
  859. glowObject.transform.parent = GameObject.transform;
  860. GameObject boltObject = new GameObject("LightningBoltMeshRendererBolt");
  861. boltObject.transform.parent = GameObject.transform;
  862. meshFilterGlow = glowObject.AddComponent<MeshFilter>();
  863. meshFilterBolt = boltObject.AddComponent<MeshFilter>();
  864. meshFilterGlow.sharedMesh = meshFilterBolt.sharedMesh = mesh;
  865. meshRendererGlow = glowObject.AddComponent<MeshRenderer>();
  866. meshRendererBolt = boltObject.AddComponent<MeshRenderer>();
  867. #if UNITY_EDITOR
  868. GameObject.hideFlags = glowObject.hideFlags = boltObject.hideFlags = HideFlags.HideAndDontSave;
  869. #endif
  870. meshRendererGlow.shadowCastingMode = meshRendererBolt.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
  871. meshRendererGlow.reflectionProbeUsage = meshRendererBolt.reflectionProbeUsage = UnityEngine.Rendering.ReflectionProbeUsage.Off;
  872. meshRendererGlow.lightProbeUsage = meshRendererBolt.lightProbeUsage = UnityEngine.Rendering.LightProbeUsage.Off;
  873. meshRendererGlow.receiveShadows = meshRendererBolt.receiveShadows = false;
  874. Transform = GameObject.GetComponent<Transform>();
  875. }, true);
  876. }
  877. /// <summary>
  878. /// Apply changes to underlying mesh
  879. /// </summary>
  880. public void PopulateMesh()
  881. {
  882. #if ENABLE_PROFILING
  883. System.Diagnostics.Stopwatch w = System.Diagnostics.Stopwatch.StartNew();
  884. #endif
  885. if (vertices.Count == 0)
  886. {
  887. mesh.Clear();
  888. }
  889. else
  890. {
  891. PopulateMeshInternal();
  892. }
  893. #if ENABLE_PROFILING
  894. Debug.LogFormat("MESH: {0}", w.Elapsed.TotalMilliseconds);
  895. #endif
  896. }
  897. /// <summary>
  898. /// Prepare for lines to be added
  899. /// </summary>
  900. /// <param name="lineCount">Lines to add</param>
  901. /// <returns>True if room for lines, false if full</returns>
  902. public bool PrepareForLines(int lineCount)
  903. {
  904. int vertexCount = lineCount * 4;
  905. if (vertices.Count + vertexCount > 64999)
  906. {
  907. return false;
  908. }
  909. return true;
  910. }
  911. /// <summary>
  912. /// Begin a line
  913. /// </summary>
  914. /// <param name="start">Start position</param>
  915. /// <param name="end">End position</param>
  916. /// <param name="radius">Radius</param>
  917. /// <param name="color">Color</param>
  918. /// <param name="colorIntensity">Color intensity</param>
  919. /// <param name="fadeLifeTime">Fade lifetime</param>
  920. /// <param name="glowWidthModifier">Glow width modifier</param>
  921. /// <param name="glowIntensity">Glow intensity</param>
  922. public void BeginLine(Vector3 start, Vector3 end, float radius, Color32 color, float colorIntensity, Vector4 fadeLifeTime, float glowWidthModifier, float glowIntensity)
  923. {
  924. Vector4 dir = (end - start);
  925. dir.w = radius;
  926. AppendLineInternal(ref start, ref end, ref dir, ref dir, ref dir, color, colorIntensity, ref fadeLifeTime, glowWidthModifier, glowIntensity);
  927. }
  928. /// <summary>
  929. /// Append a line, call multiple times after the one BeginLine call
  930. /// </summary>
  931. /// <param name="start">Start position</param>
  932. /// <param name="end">End position</param>
  933. /// <param name="radius">Radius</param>
  934. /// <param name="color">Color</param>
  935. /// <param name="colorIntensity">Color intensity</param>
  936. /// <param name="fadeLifeTime">Fade lifetime</param>
  937. /// <param name="glowWidthModifier">Glow width modifier</param>
  938. /// <param name="glowIntensity">Glow intensity</param>
  939. public void AppendLine(Vector3 start, Vector3 end, float radius, Color32 color, float colorIntensity, Vector4 fadeLifeTime, float glowWidthModifier, float glowIntensity)
  940. {
  941. Vector4 dir = (end - start);
  942. dir.w = radius;
  943. Vector4 dirPrev1 = lineDirs[lineDirs.Count - 3];
  944. Vector4 dirPrev2 = lineDirs[lineDirs.Count - 1];
  945. AppendLineInternal(ref start, ref end, ref dir, ref dirPrev1, ref dirPrev2, color, colorIntensity, ref fadeLifeTime, glowWidthModifier, glowIntensity);
  946. }
  947. /// <summary>
  948. /// Reset all state
  949. /// </summary>
  950. public void Reset()
  951. {
  952. CustomTransform = null;
  953. Tag++;
  954. GameObject.SetActive(false);
  955. mesh.Clear();
  956. indices.Clear();
  957. vertices.Clear();
  958. colors.Clear();
  959. lineDirs.Clear();
  960. ends.Clear();
  961. #if UNITY_PRE_5_3
  962. texCoords.Clear();
  963. glowModifiers.Clear();
  964. fadeXY.Clear();
  965. fadeZW.Clear();
  966. #else
  967. texCoordsAndGlowModifiers.Clear();
  968. fadeLifetimes.Clear();
  969. #endif
  970. currentBoundsMaxX = currentBoundsMaxY = currentBoundsMaxZ = int.MinValue + boundsPadder;
  971. currentBoundsMinX = currentBoundsMinY = currentBoundsMinZ = int.MaxValue - boundsPadder;
  972. }
  973. #endregion Public methods
  974. #region Private variables
  975. private const int defaultListCapacity = 2048;
  976. private static readonly Vector2 uv1 = new Vector2(0.0f, 0.0f);
  977. private static readonly Vector2 uv2 = new Vector2(1.0f, 0.0f);
  978. private static readonly Vector2 uv3 = new Vector2(0.0f, 1.0f);
  979. private static readonly Vector2 uv4 = new Vector2(1.0f, 1.0f);
  980. private readonly List<int> indices = new List<int>(defaultListCapacity);
  981. private readonly List<Vector3> vertices = new List<Vector3>(defaultListCapacity);
  982. private readonly List<Vector4> lineDirs = new List<Vector4>(defaultListCapacity);
  983. private readonly List<Color32> colors = new List<Color32>(defaultListCapacity);
  984. private readonly List<Vector3> ends = new List<Vector3>(defaultListCapacity);
  985. #if UNITY_PRE_5_3
  986. private readonly List<Vector2> texCoords = new List<Vector2>(defaultListCapacity);
  987. private readonly List<Vector2> glowModifiers = new List<Vector2>(defaultListCapacity);
  988. private readonly List<Vector2> fadeXY = new List<Vector2>(defaultListCapacity);
  989. private readonly List<Vector2> fadeZW = new List<Vector2>(defaultListCapacity);
  990. #else
  991. private readonly List<Vector4> texCoordsAndGlowModifiers = new List<Vector4>(defaultListCapacity);
  992. private readonly List<Vector4> fadeLifetimes = new List<Vector4>(defaultListCapacity);
  993. #endif
  994. private const int boundsPadder = 1000000000;
  995. private int currentBoundsMinX = int.MaxValue - boundsPadder;
  996. private int currentBoundsMinY = int.MaxValue - boundsPadder;
  997. private int currentBoundsMinZ = int.MaxValue - boundsPadder;
  998. private int currentBoundsMaxX = int.MinValue + boundsPadder;
  999. private int currentBoundsMaxY = int.MinValue + boundsPadder;
  1000. private int currentBoundsMaxZ = int.MinValue + boundsPadder;
  1001. private Mesh mesh;
  1002. private MeshFilter meshFilterGlow;
  1003. private MeshFilter meshFilterBolt;
  1004. private MeshRenderer meshRendererGlow;
  1005. private MeshRenderer meshRendererBolt;
  1006. #endregion Private variables
  1007. #region Private methods
  1008. private void PopulateMeshInternal()
  1009. {
  1010. GameObject.SetActive(true);
  1011. mesh.SetVertices(vertices);
  1012. mesh.SetTangents(lineDirs);
  1013. mesh.SetColors(colors);
  1014. mesh.SetUVs(0, texCoordsAndGlowModifiers);
  1015. mesh.SetUVs(1, fadeLifetimes);
  1016. mesh.SetNormals(ends);
  1017. mesh.SetTriangles(indices, 0);
  1018. Bounds b = new Bounds();
  1019. Vector3 min = new Vector3(currentBoundsMinX - 2, currentBoundsMinY - 2, currentBoundsMinZ - 2);
  1020. Vector3 max = new Vector3(currentBoundsMaxX + 2, currentBoundsMaxY + 2, currentBoundsMaxZ + 2);
  1021. b.center = (max + min) * 0.5f;
  1022. b.size = (max - min) * 1.2f;
  1023. mesh.bounds = b;
  1024. }
  1025. private void UpdateBounds(ref Vector3 point1, ref Vector3 point2)
  1026. {
  1027. // r = y + ((x - y) & ((x - y) >> (sizeof(int) * CHAR_BIT - 1))); // min(x, y)
  1028. // r = x - ((x - y) & ((x - y) >> (sizeof(int) * CHAR_BIT - 1))); // max(x, y)
  1029. unchecked
  1030. {
  1031. {
  1032. int xCalculation = (int)point1.x - (int)point2.x;
  1033. xCalculation &= (xCalculation >> 31);
  1034. int xMin = (int)point2.x + xCalculation;
  1035. int xMax = (int)point1.x - xCalculation;
  1036. xCalculation = currentBoundsMinX - xMin;
  1037. xCalculation &= (xCalculation >> 31);
  1038. currentBoundsMinX = xMin + xCalculation;
  1039. xCalculation = currentBoundsMaxX - xMax;
  1040. xCalculation &= (xCalculation >> 31);
  1041. currentBoundsMaxX = currentBoundsMaxX - xCalculation;
  1042. }
  1043. {
  1044. int yCalculation = (int)point1.y - (int)point2.y;
  1045. yCalculation &= (yCalculation >> 31);
  1046. int yMin = (int)point2.y + yCalculation;
  1047. int yMax = (int)point1.y - yCalculation;
  1048. yCalculation = currentBoundsMinY - yMin;
  1049. yCalculation &= (yCalculation >> 31);
  1050. currentBoundsMinY = yMin + yCalculation;
  1051. yCalculation = currentBoundsMaxY - yMax;
  1052. yCalculation &= (yCalculation >> 31);
  1053. currentBoundsMaxY = currentBoundsMaxY - yCalculation;
  1054. }
  1055. {
  1056. int zCalculation = (int)point1.z - (int)point2.z;
  1057. zCalculation &= (zCalculation >> 31);
  1058. int zMin = (int)point2.z + zCalculation;
  1059. int zMax = (int)point1.z - zCalculation;
  1060. zCalculation = currentBoundsMinZ - zMin;
  1061. zCalculation &= (zCalculation >> 31);
  1062. currentBoundsMinZ = zMin + zCalculation;
  1063. zCalculation = currentBoundsMaxZ - zMax;
  1064. zCalculation &= (zCalculation >> 31);
  1065. currentBoundsMaxZ = currentBoundsMaxZ - zCalculation;
  1066. }
  1067. }
  1068. }
  1069. private void AddIndices()
  1070. {
  1071. int vertexIndex = vertices.Count;
  1072. indices.Add(vertexIndex++);
  1073. indices.Add(vertexIndex++);
  1074. indices.Add(vertexIndex);
  1075. indices.Add(vertexIndex--);
  1076. indices.Add(vertexIndex);
  1077. indices.Add(vertexIndex += 2);
  1078. }
  1079. private void AppendLineInternal(ref Vector3 start, ref Vector3 end, ref Vector4 dir, ref Vector4 dirPrev1, ref Vector4 dirPrev2,
  1080. Color32 color, float colorIntensity, ref Vector4 fadeLifeTime, float glowWidthModifier, float glowIntensity)
  1081. {
  1082. AddIndices();
  1083. color.a = (byte)Mathf.Lerp(0.0f, 255.0f, colorIntensity * 0.1f);
  1084. Vector4 texCoord = new Vector4(uv1.x, uv1.y, glowWidthModifier, glowIntensity);
  1085. vertices.Add(start);
  1086. lineDirs.Add(dirPrev1);
  1087. colors.Add(color);
  1088. ends.Add(dir);
  1089. vertices.Add(end);
  1090. lineDirs.Add(dir);
  1091. colors.Add(color);
  1092. ends.Add(dir);
  1093. dir.w = -dir.w;
  1094. vertices.Add(start);
  1095. lineDirs.Add(dirPrev2);
  1096. colors.Add(color);
  1097. ends.Add(dir);
  1098. vertices.Add(end);
  1099. lineDirs.Add(dir);
  1100. colors.Add(color);
  1101. ends.Add(dir);
  1102. #if UNITY_PRE_5_3
  1103. texCoords.Add(uv1);
  1104. texCoords.Add(uv2);
  1105. texCoords.Add(uv3);
  1106. texCoords.Add(uv4);
  1107. glowModifiers.Add(new Vector2(texCoord.z, texCoord.w));
  1108. glowModifiers.Add(new Vector2(texCoord.z, texCoord.w));
  1109. glowModifiers.Add(new Vector2(texCoord.z, texCoord.w));
  1110. glowModifiers.Add(new Vector2(texCoord.z, texCoord.w));
  1111. fadeXY.Add(new Vector2(fadeLifeTime.x, fadeLifeTime.y));
  1112. fadeXY.Add(new Vector2(fadeLifeTime.x, fadeLifeTime.y));
  1113. fadeXY.Add(new Vector2(fadeLifeTime.x, fadeLifeTime.y));
  1114. fadeXY.Add(new Vector2(fadeLifeTime.x, fadeLifeTime.y));
  1115. fadeZW.Add(new Vector2(fadeLifeTime.z, fadeLifeTime.w));
  1116. fadeZW.Add(new Vector2(fadeLifeTime.z, fadeLifeTime.w));
  1117. fadeZW.Add(new Vector2(fadeLifeTime.z, fadeLifeTime.w));
  1118. fadeZW.Add(new Vector2(fadeLifeTime.z, fadeLifeTime.w));
  1119. #else
  1120. texCoordsAndGlowModifiers.Add(texCoord);
  1121. texCoord.x = uv2.x;
  1122. texCoord.y = uv2.y;
  1123. texCoordsAndGlowModifiers.Add(texCoord);
  1124. texCoord.x = uv3.x;
  1125. texCoord.y = uv3.y;
  1126. texCoordsAndGlowModifiers.Add(texCoord);
  1127. texCoord.x = uv4.x;
  1128. texCoord.y = uv4.y;
  1129. texCoordsAndGlowModifiers.Add(texCoord);
  1130. fadeLifetimes.Add(fadeLifeTime);
  1131. fadeLifetimes.Add(fadeLifeTime);
  1132. fadeLifetimes.Add(fadeLifeTime);
  1133. fadeLifetimes.Add(fadeLifeTime);
  1134. #endif
  1135. UpdateBounds(ref start, ref end);
  1136. }
  1137. #endregion Private methods
  1138. }
  1139. #endregion LineRendererMesh
  1140. #region Public variables
  1141. /// <summary>
  1142. /// The maximum number of lights to allow for all lightning
  1143. /// </summary>
  1144. public static int MaximumLightCount = 128;
  1145. /// <summary>
  1146. /// The maximum number of lights to create per batch of lightning emitted
  1147. /// </summary>
  1148. public static int MaximumLightsPerBatch = 8;
  1149. /// <summary>
  1150. /// The current minimum delay until anything will start rendering
  1151. /// </summary>
  1152. public float MinimumDelay { get; private set; }
  1153. /// <summary>
  1154. /// Is there any glow for any of the lightning bolts?
  1155. /// </summary>
  1156. public bool HasGlow { get; private set; }
  1157. /// <summary>
  1158. /// Is this lightning bolt active any more?
  1159. /// </summary>
  1160. public bool IsActive { get { return elapsedTime < lifeTime; } }
  1161. /// <summary>
  1162. /// Camera mode
  1163. /// </summary>
  1164. public CameraMode CameraMode { get; private set; }
  1165. private DateTime startTimeOffset;
  1166. #endregion Public variables
  1167. #region Public methods
  1168. /// <summary>
  1169. /// Default constructor
  1170. /// </summary>
  1171. public LightningBolt()
  1172. {
  1173. }
  1174. /// <summary>
  1175. /// Setup a lightning bolt from dependencies
  1176. /// </summary>
  1177. /// <param name="dependencies">Dependencies</param>
  1178. public void SetupLightningBolt(LightningBoltDependencies dependencies)
  1179. {
  1180. if (dependencies == null || dependencies.Parameters.Count == 0)
  1181. {
  1182. Debug.LogError("Lightning bolt dependencies must not be null");
  1183. return;
  1184. }
  1185. else if (this.dependencies != null)
  1186. {
  1187. Debug.LogError("This lightning bolt is already in use!");
  1188. return;
  1189. }
  1190. this.dependencies = dependencies;
  1191. CameraMode = dependencies.CameraMode;
  1192. timeSinceLevelLoad = LightningBoltScript.TimeSinceStart;
  1193. CheckForGlow(dependencies.Parameters);
  1194. MinimumDelay = float.MaxValue;
  1195. if (dependencies.ThreadState.multiThreaded)
  1196. {
  1197. startTimeOffset = DateTime.UtcNow;
  1198. dependencies.ThreadState.AddActionForBackgroundThread(ProcessAllLightningParameters);
  1199. }
  1200. else
  1201. {
  1202. ProcessAllLightningParameters();
  1203. }
  1204. }
  1205. /// <summary>
  1206. /// Update
  1207. /// </summary>
  1208. /// <returns>True if alive, false if expired</returns>
  1209. public bool Update()
  1210. {
  1211. elapsedTime += LightningBoltScript.DeltaTime;
  1212. if (elapsedTime > maxLifeTime)
  1213. {
  1214. return false;
  1215. }
  1216. else if (hasLight)
  1217. {
  1218. UpdateLights();
  1219. }
  1220. return true;
  1221. }
  1222. /// <summary>
  1223. /// Cleanup all resources
  1224. /// </summary>
  1225. public void Cleanup()
  1226. {
  1227. foreach (LightningBoltSegmentGroup g in segmentGroupsWithLight)
  1228. {
  1229. // cleanup lights
  1230. foreach (Light l in g.Lights)
  1231. {
  1232. CleanupLight(l);
  1233. }
  1234. g.Lights.Clear();
  1235. }
  1236. lock (groupCache)
  1237. {
  1238. foreach (LightningBoltSegmentGroup g in segmentGroups)
  1239. {
  1240. groupCache.Add(g);
  1241. }
  1242. }
  1243. hasLight = false;
  1244. elapsedTime = 0.0f;
  1245. lifeTime = 0.0f;
  1246. maxLifeTime = 0.0f;
  1247. if (dependencies != null)
  1248. {
  1249. dependencies.ReturnToCache(dependencies);
  1250. dependencies = null;
  1251. }
  1252. // return all line renderers to cache
  1253. foreach (LineRendererMesh m in activeLineRenderers)
  1254. {
  1255. if (m != null)
  1256. {
  1257. m.Reset();
  1258. lineRendererCache.Add(m);
  1259. }
  1260. }
  1261. segmentGroups.Clear();
  1262. segmentGroupsWithLight.Clear();
  1263. activeLineRenderers.Clear();
  1264. }
  1265. /// <summary>
  1266. /// Add a new segment group, or get one from cache
  1267. /// </summary>
  1268. /// <returns>LightningBoltSegmentGroup</returns>
  1269. public LightningBoltSegmentGroup AddGroup()
  1270. {
  1271. LightningBoltSegmentGroup group;
  1272. lock (groupCache)
  1273. {
  1274. if (groupCache.Count == 0)
  1275. {
  1276. group = new LightningBoltSegmentGroup();
  1277. }
  1278. else
  1279. {
  1280. int index = groupCache.Count - 1;
  1281. group = groupCache[index];
  1282. group.Reset();
  1283. groupCache.RemoveAt(index);
  1284. }
  1285. }
  1286. segmentGroups.Add(group);
  1287. return group;
  1288. }
  1289. /// <summary>
  1290. /// Clear out all cached objects to free up memory
  1291. /// </summary>
  1292. public static void ClearCache()
  1293. {
  1294. foreach (LineRendererMesh obj in lineRendererCache)
  1295. {
  1296. if (obj != null)
  1297. {
  1298. GameObject.Destroy(obj.GameObject);
  1299. }
  1300. }
  1301. foreach (Light obj in lightCache)
  1302. {
  1303. if (obj != null)
  1304. {
  1305. GameObject.Destroy(obj.gameObject);
  1306. }
  1307. }
  1308. lineRendererCache.Clear();
  1309. lightCache.Clear();
  1310. lock (groupCache)
  1311. {
  1312. groupCache.Clear();
  1313. }
  1314. }
  1315. #endregion Public methods
  1316. #region Private variables
  1317. // required dependencies to create lightning bolts
  1318. private LightningBoltDependencies dependencies;
  1319. // how long this bolt has been alive
  1320. private float elapsedTime;
  1321. // total life span of this bolt
  1322. private float lifeTime;
  1323. // either lifeTime or larger depending on if lights are lingering beyond the end of the bolt
  1324. private float maxLifeTime;
  1325. // does this lightning bolt have light?
  1326. private bool hasLight;
  1327. // saved in case of threading
  1328. private float timeSinceLevelLoad;
  1329. private readonly List<LightningBoltSegmentGroup> segmentGroups = new List<LightningBoltSegmentGroup>();
  1330. private readonly List<LightningBoltSegmentGroup> segmentGroupsWithLight = new List<LightningBoltSegmentGroup>();
  1331. private readonly List<LineRendererMesh> activeLineRenderers = new List<LineRendererMesh>();
  1332. private static int lightCount;
  1333. private static readonly List<LineRendererMesh> lineRendererCache = new List<LineRendererMesh>();
  1334. private static readonly List<LightningBoltSegmentGroup> groupCache = new List<LightningBoltSegmentGroup>();
  1335. private static readonly List<Light> lightCache = new List<Light>();
  1336. #endregion Private variables
  1337. #region Private methods
  1338. private void CleanupLight(Light l)
  1339. {
  1340. if (l != null)
  1341. {
  1342. dependencies.LightRemoved(l);
  1343. lightCache.Add(l);
  1344. l.gameObject.SetActive(false);
  1345. lightCount--;
  1346. }
  1347. }
  1348. private void EnableLineRenderer(LineRendererMesh lineRenderer, int tag)
  1349. {
  1350. bool shouldPopulate = (lineRenderer != null && lineRenderer.GameObject != null && lineRenderer.Tag == tag && IsActive);
  1351. if (shouldPopulate)
  1352. {
  1353. lineRenderer.PopulateMesh();
  1354. }
  1355. }
  1356. private IEnumerator EnableLastRendererCoRoutine()
  1357. {
  1358. LineRendererMesh lineRenderer = activeLineRenderers[activeLineRenderers.Count - 1];
  1359. int tag = ++lineRenderer.Tag; // in case it gets cleaned up for later
  1360. yield return WaitForSecondsLightning.WaitForSecondsLightningPooled(MinimumDelay);
  1361. EnableLineRenderer(lineRenderer, tag);
  1362. }
  1363. private LineRendererMesh GetOrCreateLineRenderer()
  1364. {
  1365. LineRendererMesh lineRenderer;
  1366. while (true)
  1367. {
  1368. if (lineRendererCache.Count == 0)
  1369. {
  1370. lineRenderer = new LineRendererMesh(this.dependencies);
  1371. }
  1372. else
  1373. {
  1374. int index = lineRendererCache.Count - 1;
  1375. lineRenderer = lineRendererCache[index];
  1376. lineRendererCache.RemoveAt(index);
  1377. if (lineRenderer == null || lineRenderer.Transform == null)
  1378. {
  1379. // destroyed by some other means, try again for cache...
  1380. continue;
  1381. }
  1382. }
  1383. break;
  1384. }
  1385. dependencies.ThreadState.AddActionForMainThread(b =>
  1386. {
  1387. // clear parent - this ensures that the rotation and scale can be reset before assigning a new parent
  1388. lineRenderer.Transform.parent = null;
  1389. lineRenderer.Transform.rotation = Quaternion.identity;
  1390. lineRenderer.Transform.localScale = Vector3.one;
  1391. lineRenderer.Transform.parent = dependencies.Parent.transform;
  1392. lineRenderer.GameObject.layer = lineRenderer.MeshRendererBolt.gameObject.layer = lineRenderer.MeshRendererGlow.gameObject.layer = dependencies.Parent.layer; // maintain the layer of the parent
  1393. if (dependencies.UseWorldSpace)
  1394. {
  1395. lineRenderer.GameObject.transform.position = Vector3.zero;
  1396. }
  1397. else
  1398. {
  1399. lineRenderer.GameObject.transform.localPosition = Vector3.zero;
  1400. }
  1401. lineRenderer.MaterialGlow = dependencies.LightningMaterialMesh;
  1402. lineRenderer.MaterialBolt = dependencies.LightningMaterialMeshNoGlow;
  1403. if (!string.IsNullOrEmpty(dependencies.SortLayerName))
  1404. {
  1405. lineRenderer.MeshRendererGlow.sortingLayerName = lineRenderer.MeshRendererBolt.sortingLayerName = dependencies.SortLayerName;
  1406. lineRenderer.MeshRendererGlow.sortingOrder = lineRenderer.MeshRendererBolt.sortingOrder = dependencies.SortOrderInLayer;
  1407. }
  1408. else
  1409. {
  1410. lineRenderer.MeshRendererGlow.sortingLayerName = lineRenderer.MeshRendererBolt.sortingLayerName = null;
  1411. lineRenderer.MeshRendererGlow.sortingOrder = lineRenderer.MeshRendererBolt.sortingOrder = 0;
  1412. }
  1413. }, true);
  1414. activeLineRenderers.Add(lineRenderer);
  1415. return lineRenderer;
  1416. }
  1417. private void RenderGroup(LightningBoltSegmentGroup group, LightningBoltParameters p)
  1418. {
  1419. if (group.SegmentCount == 0)
  1420. {
  1421. return;
  1422. }
  1423. float timeOffset = (!dependencies.ThreadState.multiThreaded ? 0.0f : (float)(DateTime.UtcNow - startTimeOffset).TotalSeconds);
  1424. float timeStart = timeSinceLevelLoad + group.Delay + timeOffset;
  1425. Vector4 fadeLifeTime = new Vector4(timeStart, timeStart + group.PeakStart, timeStart + group.PeakEnd, timeStart + group.LifeTime);
  1426. float radius = group.LineWidth * 0.5f * LightningBoltParameters.Scale;
  1427. int lineCount = (group.Segments.Count - group.StartIndex);
  1428. float radiusStep = (radius - (radius * group.EndWidthMultiplier)) / (float)lineCount;
  1429. // growth multiplier
  1430. float timeStep;
  1431. if (p.GrowthMultiplier > 0.0f)
  1432. {
  1433. timeStep = (group.LifeTime / (float)lineCount) * p.GrowthMultiplier;
  1434. timeOffset = 0.0f;
  1435. }
  1436. else
  1437. {
  1438. timeStep = 0.0f;
  1439. timeOffset = 0.0f;
  1440. }
  1441. LineRendererMesh currentLineRenderer = (activeLineRenderers.Count == 0 ? GetOrCreateLineRenderer() : activeLineRenderers[activeLineRenderers.Count - 1]);
  1442. // if we have filled up the mesh, we need to start a new line renderer
  1443. if (!currentLineRenderer.PrepareForLines(lineCount))
  1444. {
  1445. if (currentLineRenderer.CustomTransform != null)
  1446. {
  1447. // can't create multiple meshes if using a custom transform callback
  1448. return;
  1449. }
  1450. if (dependencies.ThreadState.multiThreaded)
  1451. {
  1452. // we need to block until this action is run, Unity objects can only be modified and created on the main thread
  1453. dependencies.ThreadState.AddActionForMainThread((inDestroy) =>
  1454. {
  1455. if (!inDestroy)
  1456. {
  1457. EnableCurrentLineRenderer();
  1458. currentLineRenderer = GetOrCreateLineRenderer();
  1459. }
  1460. }, true);
  1461. }
  1462. else
  1463. {
  1464. EnableCurrentLineRenderer();
  1465. currentLineRenderer = GetOrCreateLineRenderer();
  1466. }
  1467. }
  1468. currentLineRenderer.BeginLine(group.Segments[group.StartIndex].Start, group.Segments[group.StartIndex].End, radius, group.Color, p.Intensity, fadeLifeTime, p.GlowWidthMultiplier, p.GlowIntensity);
  1469. for (int i = group.StartIndex + 1; i < group.Segments.Count; i++)
  1470. {
  1471. radius -= radiusStep;
  1472. if (p.GrowthMultiplier < 1.0f)
  1473. {
  1474. timeOffset += timeStep;
  1475. fadeLifeTime = new Vector4(timeStart + timeOffset, timeStart + group.PeakStart + timeOffset, timeStart + group.PeakEnd, timeStart + group.LifeTime);
  1476. }
  1477. currentLineRenderer.AppendLine(group.Segments[i].Start, group.Segments[i].End, radius, group.Color, p.Intensity, fadeLifeTime, p.GlowWidthMultiplier, p.GlowIntensity);
  1478. }
  1479. }
  1480. private static IEnumerator NotifyBolt(LightningBoltDependencies dependencies, LightningBoltParameters p, Transform transform, Vector3 start, Vector3 end)
  1481. {
  1482. float delay = p.delaySeconds;
  1483. float lifeTime = p.LifeTime;
  1484. yield return WaitForSecondsLightning.WaitForSecondsLightningPooled(delay);
  1485. if (dependencies.LightningBoltStarted != null)
  1486. {
  1487. dependencies.LightningBoltStarted(p, start, end);
  1488. }
  1489. LightningCustomTransformStateInfo state = (p.CustomTransform == null ? null : LightningCustomTransformStateInfo.GetOrCreateStateInfo());
  1490. if (state != null)
  1491. {
  1492. state.Parameters = p;
  1493. state.BoltStartPosition = start;
  1494. state.BoltEndPosition = end;
  1495. state.State = LightningCustomTransformState.Started;
  1496. state.Transform = transform;
  1497. p.CustomTransform(state);
  1498. state.State = LightningCustomTransformState.Executing;
  1499. }
  1500. if (p.CustomTransform == null)
  1501. {
  1502. yield return WaitForSecondsLightning.WaitForSecondsLightningPooled(lifeTime);
  1503. }
  1504. else
  1505. {
  1506. while (lifeTime > 0.0f)
  1507. {
  1508. p.CustomTransform(state);
  1509. lifeTime -= LightningBoltScript.DeltaTime;
  1510. yield return null;
  1511. }
  1512. }
  1513. if (p.CustomTransform != null)
  1514. {
  1515. state.State = LightningCustomTransformState.Ended;
  1516. p.CustomTransform(state);
  1517. LightningCustomTransformStateInfo.ReturnStateInfoToCache(state);
  1518. }
  1519. if (dependencies.LightningBoltEnded != null)
  1520. {
  1521. dependencies.LightningBoltEnded(p, start, end);
  1522. }
  1523. LightningBoltParameters.ReturnParametersToCache(p);
  1524. }
  1525. private void ProcessParameters(LightningBoltParameters p, RangeOfFloats delay, LightningBoltDependencies depends)
  1526. {
  1527. Vector3 start, end;
  1528. MinimumDelay = Mathf.Min(delay.Minimum, MinimumDelay);
  1529. p.delaySeconds = delay.Random(p.Random);
  1530. // apply LOD if specified
  1531. if (depends.LevelOfDetailDistance > Mathf.Epsilon)
  1532. {
  1533. float d;
  1534. if (p.Points.Count > 1)
  1535. {
  1536. d = Vector3.Distance(depends.CameraPos, p.Points[0]);
  1537. d = Mathf.Min(Vector3.Distance(depends.CameraPos, p.Points[p.Points.Count - 1]));
  1538. }
  1539. else
  1540. {
  1541. d = Vector3.Distance(depends.CameraPos, p.Start);
  1542. d = Mathf.Min(Vector3.Distance(depends.CameraPos, p.End));
  1543. }
  1544. int modifier = Mathf.Min(8, (int)(d / depends.LevelOfDetailDistance));
  1545. p.Generations = Mathf.Max(1, p.Generations - modifier);
  1546. p.GenerationWhereForksStopSubtractor = Mathf.Clamp(p.GenerationWhereForksStopSubtractor - modifier, 0, 8);
  1547. }
  1548. p.generationWhereForksStop = p.Generations - p.GenerationWhereForksStopSubtractor;
  1549. lifeTime = Mathf.Max(p.LifeTime + p.delaySeconds, lifeTime);
  1550. maxLifeTime = Mathf.Max(lifeTime, maxLifeTime);
  1551. p.forkednessCalculated = (int)Mathf.Ceil(p.Forkedness * (float)p.Generations);
  1552. if (p.Generations > 0)
  1553. {
  1554. p.Generator = p.Generator ?? LightningGenerator.GeneratorInstance;
  1555. p.Generator.GenerateLightningBolt(this, p, out start, out end);
  1556. p.Start = start;
  1557. p.End = end;
  1558. }
  1559. }
  1560. private void ProcessAllLightningParameters()
  1561. {
  1562. int maxLightsForEachParameters = MaximumLightsPerBatch / dependencies.Parameters.Count;
  1563. RangeOfFloats delay = new RangeOfFloats();
  1564. List<int> groupIndexes = new List<int>(dependencies.Parameters.Count + 1);
  1565. int i = 0;
  1566. #if ENABLE_PROFILING
  1567. System.Diagnostics.Stopwatch w = System.Diagnostics.Stopwatch.StartNew();
  1568. #endif
  1569. foreach (LightningBoltParameters parameters in dependencies.Parameters)
  1570. {
  1571. delay.Minimum = parameters.DelayRange.Minimum + parameters.Delay;
  1572. delay.Maximum = parameters.DelayRange.Maximum + parameters.Delay;
  1573. parameters.maxLights = maxLightsForEachParameters;
  1574. groupIndexes.Add(segmentGroups.Count);
  1575. ProcessParameters(parameters, delay, dependencies);
  1576. }
  1577. groupIndexes.Add(segmentGroups.Count);
  1578. #if ENABLE_PROFILING
  1579. w.Stop();
  1580. UnityEngine.Debug.LogFormat("GENERATE: {0}", w.Elapsed.TotalMilliseconds);
  1581. w.Reset();
  1582. w.Start();
  1583. #endif
  1584. LightningBoltDependencies dependenciesRef = dependencies;
  1585. foreach (LightningBoltParameters parameters in dependenciesRef.Parameters)
  1586. {
  1587. Transform transform = RenderLightningBolt(parameters.quality, parameters.Generations, groupIndexes[i], groupIndexes[++i], parameters);
  1588. if (dependenciesRef.ThreadState.multiThreaded)
  1589. {
  1590. dependenciesRef.ThreadState.AddActionForMainThread((inDestroy) =>
  1591. {
  1592. if (!inDestroy)
  1593. {
  1594. dependenciesRef.StartCoroutine(NotifyBolt(dependenciesRef, parameters, transform, parameters.Start, parameters.End));
  1595. }
  1596. }, false);
  1597. }
  1598. else
  1599. {
  1600. dependenciesRef.StartCoroutine(NotifyBolt(dependenciesRef, parameters, transform, parameters.Start, parameters.End));
  1601. }
  1602. }
  1603. #if ENABLE_PROFILING
  1604. w.Stop();
  1605. UnityEngine.Debug.LogFormat("RENDER: {0}", w.Elapsed.TotalMilliseconds);
  1606. #endif
  1607. if (dependencies.ThreadState.multiThreaded)
  1608. {
  1609. dependencies.ThreadState.AddActionForMainThread(EnableCurrentLineRendererFromThread);
  1610. }
  1611. else
  1612. {
  1613. EnableCurrentLineRenderer();
  1614. dependencies.AddActiveBolt(this);
  1615. }
  1616. }
  1617. private void EnableCurrentLineRendererFromThread(bool inDestroy)
  1618. {
  1619. //try
  1620. //{
  1621. if (inDestroy)
  1622. {
  1623. return;
  1624. }
  1625. EnableCurrentLineRenderer();
  1626. dependencies.AddActiveBolt(this);
  1627. //}
  1628. //finally
  1629. //{
  1630. // clear the thread state, we verify in the Cleanup method that this is nulled out to ensure we are not cleaning up lightning that is still being generated
  1631. //dependencies.ThreadState = null;
  1632. //}
  1633. }
  1634. private void EnableCurrentLineRenderer()
  1635. {
  1636. if (activeLineRenderers.Count == 0)
  1637. {
  1638. return;
  1639. }
  1640. // make sure the last renderer gets enabled at the appropriate time
  1641. else if (MinimumDelay <= 0.0f)
  1642. {
  1643. EnableLineRenderer(activeLineRenderers[activeLineRenderers.Count - 1], activeLineRenderers[activeLineRenderers.Count - 1].Tag);
  1644. }
  1645. else
  1646. {
  1647. dependencies.StartCoroutine(EnableLastRendererCoRoutine());
  1648. }
  1649. }
  1650. private void RenderParticleSystems(Vector3 start, Vector3 end, float trunkWidth, float lifeTime, float delaySeconds)
  1651. {
  1652. // only emit particle systems if we have a trunk - example, cloud lightning should not emit particles
  1653. if (trunkWidth > 0.0f)
  1654. {
  1655. if (dependencies.OriginParticleSystem != null)
  1656. {
  1657. // we have a strike, create a particle where the lightning is coming from
  1658. dependencies.StartCoroutine(GenerateParticleCoRoutine(dependencies.OriginParticleSystem, start, delaySeconds));
  1659. }
  1660. if (dependencies.DestParticleSystem != null)
  1661. {
  1662. dependencies.StartCoroutine(GenerateParticleCoRoutine(dependencies.DestParticleSystem, end, delaySeconds + (lifeTime * 0.8f)));
  1663. }
  1664. }
  1665. }
  1666. private Transform RenderLightningBolt(LightningBoltQualitySetting quality, int generations, int startGroupIndex, int endGroupIndex, LightningBoltParameters parameters)
  1667. {
  1668. if (segmentGroups.Count == 0 || startGroupIndex >= segmentGroups.Count || endGroupIndex > segmentGroups.Count)
  1669. {
  1670. return null;
  1671. }
  1672. Transform transform = null;
  1673. LightningLightParameters lp = parameters.LightParameters;
  1674. if (lp != null)
  1675. {
  1676. if ((hasLight |= lp.HasLight))
  1677. {
  1678. lp.LightPercent = Mathf.Clamp(lp.LightPercent, Mathf.Epsilon, 1.0f);
  1679. lp.LightShadowPercent = Mathf.Clamp(lp.LightShadowPercent, 0.0f, 1.0f);
  1680. }
  1681. else
  1682. {
  1683. lp = null;
  1684. }
  1685. }
  1686. LightningBoltSegmentGroup mainTrunkGroup = segmentGroups[startGroupIndex];
  1687. Vector3 start = mainTrunkGroup.Segments[mainTrunkGroup.StartIndex].Start;
  1688. Vector3 end = mainTrunkGroup.Segments[mainTrunkGroup.StartIndex + mainTrunkGroup.SegmentCount - 1].End;
  1689. parameters.FadePercent = Mathf.Clamp(parameters.FadePercent, 0.0f, 0.5f);
  1690. // create a new line renderer mesh right now if we have a custom transform
  1691. if (parameters.CustomTransform != null)
  1692. {
  1693. LineRendererMesh currentLineRenderer = (activeLineRenderers.Count == 0 || !activeLineRenderers[activeLineRenderers.Count - 1].Empty ? null : activeLineRenderers[activeLineRenderers.Count - 1]);
  1694. if (currentLineRenderer == null)
  1695. {
  1696. if (dependencies.ThreadState.multiThreaded)
  1697. {
  1698. // we need to block until this action is run, Unity objects can only be modified and created on the main thread
  1699. dependencies.ThreadState.AddActionForMainThread((inDestroy) =>
  1700. {
  1701. if (!inDestroy)
  1702. {
  1703. EnableCurrentLineRenderer();
  1704. currentLineRenderer = GetOrCreateLineRenderer();
  1705. }
  1706. }, true);
  1707. }
  1708. else
  1709. {
  1710. EnableCurrentLineRenderer();
  1711. currentLineRenderer = GetOrCreateLineRenderer();
  1712. }
  1713. }
  1714. if (currentLineRenderer == null)
  1715. {
  1716. return null;
  1717. }
  1718. currentLineRenderer.CustomTransform = parameters.CustomTransform;
  1719. transform = currentLineRenderer.Transform;
  1720. }
  1721. for (int i = startGroupIndex; i < endGroupIndex; i++)
  1722. {
  1723. LightningBoltSegmentGroup group = segmentGroups[i];
  1724. group.Delay = parameters.delaySeconds;
  1725. group.LifeTime = parameters.LifeTime;
  1726. group.PeakStart = group.LifeTime * parameters.FadePercent;
  1727. group.PeakEnd = group.LifeTime - group.PeakStart;
  1728. float peakGap = group.PeakEnd - group.PeakStart;
  1729. float fadeOut = group.LifeTime - group.PeakEnd;
  1730. group.PeakStart *= parameters.FadeInMultiplier;
  1731. group.PeakEnd = group.PeakStart + (peakGap * parameters.FadeFullyLitMultiplier);
  1732. group.LifeTime = group.PeakEnd + (fadeOut * parameters.FadeOutMultiplier);
  1733. group.LightParameters = lp;
  1734. RenderGroup(group, parameters);
  1735. }
  1736. if (dependencies.ThreadState.multiThreaded)
  1737. {
  1738. dependencies.ThreadState.AddActionForMainThread((inDestroy) =>
  1739. {
  1740. if (!inDestroy)
  1741. {
  1742. RenderParticleSystems(start, end, parameters.TrunkWidth, parameters.LifeTime, parameters.delaySeconds);
  1743. // create lights only on the main trunk
  1744. if (lp != null)
  1745. {
  1746. CreateLightsForGroup(segmentGroups[startGroupIndex], lp, quality, parameters.maxLights);
  1747. }
  1748. }
  1749. });
  1750. }
  1751. else
  1752. {
  1753. RenderParticleSystems(start, end, parameters.TrunkWidth, parameters.LifeTime, parameters.delaySeconds);
  1754. // create lights only on the main trunk
  1755. if (lp != null)
  1756. {
  1757. CreateLightsForGroup(segmentGroups[startGroupIndex], lp, quality, parameters.maxLights);
  1758. }
  1759. }
  1760. return transform;
  1761. }
  1762. private void CreateLightsForGroup(LightningBoltSegmentGroup group, LightningLightParameters lp, LightningBoltQualitySetting quality, int maxLights)
  1763. {
  1764. if (lightCount == MaximumLightCount || maxLights <= 0)
  1765. {
  1766. return;
  1767. }
  1768. float fadeOutTime = (lifeTime - group.PeakEnd) * lp.FadeOutMultiplier;
  1769. float peakGap = (group.PeakEnd - group.PeakStart) * lp.FadeFullyLitMultiplier;
  1770. float peakStart = group.PeakStart * lp.FadeInMultiplier;
  1771. float peakEnd = peakStart + peakGap;
  1772. float maxLifeWithLights = peakEnd + fadeOutTime;
  1773. maxLifeTime = Mathf.Max(maxLifeTime, group.Delay + maxLifeWithLights);
  1774. segmentGroupsWithLight.Add(group);
  1775. int segmentCount = group.SegmentCount;
  1776. float lightPercent, lightShadowPercent;
  1777. if (quality == LightningBoltQualitySetting.LimitToQualitySetting)
  1778. {
  1779. int level = QualitySettings.GetQualityLevel();
  1780. LightningQualityMaximum maximum;
  1781. if (LightningBoltParameters.QualityMaximums.TryGetValue(level, out maximum))
  1782. {
  1783. lightPercent = Mathf.Min(lp.LightPercent, maximum.MaximumLightPercent);
  1784. lightShadowPercent = Mathf.Min(lp.LightShadowPercent, maximum.MaximumShadowPercent);
  1785. }
  1786. else
  1787. {
  1788. Debug.LogError("Unable to read lightning quality for level " + level.ToString());
  1789. lightPercent = lp.LightPercent;
  1790. lightShadowPercent = lp.LightShadowPercent;
  1791. }
  1792. }
  1793. else
  1794. {
  1795. lightPercent = lp.LightPercent;
  1796. lightShadowPercent = lp.LightShadowPercent;
  1797. }
  1798. maxLights = Mathf.Max(1, Mathf.Min(maxLights, (int)(segmentCount * lightPercent)));
  1799. int nthLight = Mathf.Max(1, (int)((segmentCount / maxLights)));
  1800. int nthShadows = maxLights - (int)((float)maxLights * lightShadowPercent);
  1801. int nthShadowCounter = nthShadows;
  1802. // add lights evenly spaced
  1803. for (int i = group.StartIndex + (int)(nthLight * 0.5f); i < group.Segments.Count; i += nthLight)
  1804. {
  1805. if (AddLightToGroup(group, lp, i, nthLight, nthShadows, ref maxLights, ref nthShadowCounter))
  1806. {
  1807. return;
  1808. }
  1809. }
  1810. // Debug.Log("Lightning light count: " + lightCount.ToString());
  1811. }
  1812. private bool AddLightToGroup(LightningBoltSegmentGroup group, LightningLightParameters lp, int segmentIndex,
  1813. int nthLight, int nthShadows, ref int maxLights, ref int nthShadowCounter)
  1814. {
  1815. Light light = GetOrCreateLight(lp);
  1816. group.Lights.Add(light);
  1817. Vector3 pos = (group.Segments[segmentIndex].Start + group.Segments[segmentIndex].End) * 0.5f;
  1818. if (dependencies.CameraIsOrthographic)
  1819. {
  1820. if (dependencies.CameraMode == CameraMode.OrthographicXZ)
  1821. {
  1822. pos.y = dependencies.CameraPos.y + lp.OrthographicOffset;
  1823. }
  1824. else
  1825. {
  1826. pos.z = dependencies.CameraPos.z + lp.OrthographicOffset;
  1827. }
  1828. }
  1829. if (dependencies.UseWorldSpace)
  1830. {
  1831. light.gameObject.transform.position = pos;
  1832. }
  1833. else
  1834. {
  1835. light.gameObject.transform.localPosition = pos;
  1836. }
  1837. if (lp.LightShadowPercent == 0.0f || ++nthShadowCounter < nthShadows)
  1838. {
  1839. light.shadows = LightShadows.None;
  1840. }
  1841. else
  1842. {
  1843. light.shadows = LightShadows.Soft;
  1844. nthShadowCounter = 0;
  1845. }
  1846. // return true if no more lights possible, false otherwise
  1847. return (++lightCount == MaximumLightCount || --maxLights == 0);
  1848. }
  1849. private Light GetOrCreateLight(LightningLightParameters lp)
  1850. {
  1851. Light light;
  1852. while (true)
  1853. {
  1854. if (lightCache.Count == 0)
  1855. {
  1856. GameObject lightningLightObject = new GameObject("LightningBoltLight");
  1857. #if UNITY_EDITOR
  1858. lightningLightObject.hideFlags = HideFlags.HideInInspector | HideFlags.HideInHierarchy;
  1859. #endif
  1860. light = lightningLightObject.AddComponent<Light>();
  1861. light.type = LightType.Point;
  1862. break;
  1863. }
  1864. else
  1865. {
  1866. light = lightCache[lightCache.Count - 1];
  1867. lightCache.RemoveAt(lightCache.Count - 1);
  1868. if (light == null)
  1869. {
  1870. // may have been disposed or the level re-loaded
  1871. continue;
  1872. }
  1873. break;
  1874. }
  1875. }
  1876. light.bounceIntensity = lp.BounceIntensity;
  1877. light.shadowNormalBias = lp.ShadowNormalBias;
  1878. light.color = lp.LightColor;
  1879. light.renderMode = lp.RenderMode;
  1880. light.range = lp.LightRange;
  1881. light.shadowStrength = lp.ShadowStrength;
  1882. light.shadowBias = lp.ShadowBias;
  1883. light.intensity = 0.0f;
  1884. light.gameObject.transform.parent = dependencies.Parent.transform;
  1885. light.gameObject.SetActive(true);
  1886. dependencies.LightAdded(light);
  1887. return light;
  1888. }
  1889. private void UpdateLight(LightningLightParameters lp, IEnumerable<Light> lights, float delay, float peakStart, float peakEnd, float lifeTime)
  1890. {
  1891. if (elapsedTime < delay)
  1892. {
  1893. return;
  1894. }
  1895. // depending on whether we have hit the mid point of our lifetime, fade the light in or out
  1896. // adjust lights for fade parameters
  1897. float fadeOutTime = (lifeTime - peakEnd) * lp.FadeOutMultiplier;
  1898. float peakGap = (peakEnd - peakStart) * lp.FadeFullyLitMultiplier;
  1899. peakStart *= lp.FadeInMultiplier;
  1900. peakEnd = peakStart + peakGap;
  1901. lifeTime = peakEnd + fadeOutTime;
  1902. float realElapsedTime = elapsedTime - delay;
  1903. if (realElapsedTime >= peakStart)
  1904. {
  1905. if (realElapsedTime <= peakEnd)
  1906. {
  1907. // fully lit
  1908. foreach (Light l in lights)
  1909. {
  1910. l.intensity = lp.LightIntensity * lp.LightMultiplier;
  1911. }
  1912. }
  1913. else
  1914. {
  1915. // fading out
  1916. float lerp = (realElapsedTime - peakEnd) / (lifeTime - peakEnd);
  1917. foreach (Light l in lights)
  1918. {
  1919. l.intensity = Mathf.Lerp(lp.LightIntensity * lp.LightMultiplier, 0.0f, lerp);
  1920. }
  1921. }
  1922. }
  1923. else
  1924. {
  1925. // fading in
  1926. float lerp = realElapsedTime / peakStart;
  1927. foreach (Light l in lights)
  1928. {
  1929. l.intensity = Mathf.Lerp(0.0f, lp.LightIntensity * lp.LightMultiplier, lerp);
  1930. }
  1931. }
  1932. }
  1933. private void UpdateLights()
  1934. {
  1935. foreach (LightningBoltSegmentGroup group in segmentGroupsWithLight)
  1936. {
  1937. UpdateLight(group.LightParameters, group.Lights, group.Delay, group.PeakStart, group.PeakEnd, group.LifeTime);
  1938. }
  1939. }
  1940. private IEnumerator GenerateParticleCoRoutine(ParticleSystem p, Vector3 pos, float delay)
  1941. {
  1942. yield return WaitForSecondsLightning.WaitForSecondsLightningPooled(delay);
  1943. p.transform.position = pos;
  1944. int count;
  1945. if (p.emission.burstCount > 0)
  1946. {
  1947. ParticleSystem.Burst[] bursts = new ParticleSystem.Burst[p.emission.burstCount];
  1948. p.emission.GetBursts(bursts);
  1949. count = UnityEngine.Random.Range(bursts[0].minCount, bursts[0].maxCount + 1);
  1950. p.Emit(count);
  1951. }
  1952. else
  1953. {
  1954. ParticleSystem.MinMaxCurve rate = p.emission.rateOverTime;
  1955. count = (int)((rate.constantMax - rate.constantMin) * 0.5f);
  1956. count = UnityEngine.Random.Range(count, count * 2);
  1957. p.Emit(count);
  1958. }
  1959. }
  1960. private void CheckForGlow(IEnumerable<LightningBoltParameters> parameters)
  1961. {
  1962. // we need to know if there is glow so we can choose the glow or non-glow setting in the renderer
  1963. foreach (LightningBoltParameters p in parameters)
  1964. {
  1965. HasGlow = (p.GlowIntensity >= Mathf.Epsilon && p.GlowWidthMultiplier >= Mathf.Epsilon);
  1966. if (HasGlow)
  1967. {
  1968. break;
  1969. }
  1970. }
  1971. }
  1972. #endregion Private methods
  1973. }
  1974. #if UNITY_WEBGL
  1975. public class LightningThreadState
  1976. {
  1977. internal readonly int mainThreadId = 1;
  1978. internal readonly bool multiThreaded;
  1979. /// <summary>
  1980. /// Running?
  1981. /// </summary>
  1982. public bool Running { get; set; }
  1983. /// <summary>
  1984. /// Constructor - starts the thread
  1985. /// </summary>
  1986. /// <param name="multiThreaded">Multi-threaded?</param>
  1987. public LightningThreadState(bool multiThreaded)
  1988. {
  1989. this.multiThreaded = false;
  1990. }
  1991. /// <summary>
  1992. /// Add a main thread action
  1993. /// </summary>
  1994. /// <param name="action">Action</param>
  1995. /// <param name="waitForAction">True to wait for completion, false if not</param>
  1996. /// <returns>True if action added, false if in process of terminating the thread</returns>
  1997. public bool AddActionForMainThread(System.Action<bool> action, bool waitForAction = false)
  1998. {
  1999. action(false);
  2000. return true;
  2001. }
  2002. /// <summary>
  2003. /// Terminate and wait for thread end
  2004. /// </summary>
  2005. /// <param name="inDestroy">True if in destroy, false otherwise</param>
  2006. public void TerminateAndWaitForEnd(bool inDestroy)
  2007. {
  2008. }
  2009. /// <summary>
  2010. /// Add a background thread action
  2011. /// </summary>
  2012. /// <param name="action">Action</param>
  2013. /// <returns>True if action added, false if in process of terminating the thread</returns>
  2014. public bool AddActionForBackgroundThread(System.Action action)
  2015. {
  2016. action();
  2017. return true;
  2018. }
  2019. public void UpdateMainThreadActions()
  2020. {
  2021. }
  2022. }
  2023. #else
  2024. /// <summary>
  2025. /// Lightning threading state
  2026. /// </summary>
  2027. public class LightningThreadState
  2028. {
  2029. private const int maxTimeoutWaitMainThread = 30000;
  2030. // needs to be thread safe
  2031. private static readonly BlockingCollection<AutoResetEvent> autoResetEventPool = new BlockingCollection<AutoResetEvent>();
  2032. internal readonly int mainThreadId;
  2033. internal readonly bool multiThreaded;
  2034. #if TASK_AVAILABLE
  2035. private Task lightningThread;
  2036. #else
  2037. /// <summary>
  2038. /// Lightning thread
  2039. /// </summary>
  2040. private Thread lightningThread;
  2041. #endif
  2042. /// <summary>
  2043. /// List of background actions
  2044. /// </summary>
  2045. private readonly BlockingCollection<System.Action> actionsForBackgroundThread = new BlockingCollection<System.Action>(new ConcurrentQueue<System.Action>());
  2046. /// <summary>
  2047. /// List of main thread actions and optional events to signal
  2048. /// </summary>
  2049. private readonly BlockingCollection<(System.Action<bool> action, AutoResetEvent evt)> actionsForMainThread = new BlockingCollection<(System.Action<bool>, AutoResetEvent)>(new ConcurrentQueue<(System.Action<bool>, AutoResetEvent)>());
  2050. /// <summary>
  2051. /// Set to false to terminate
  2052. /// </summary>
  2053. public bool Running = true;
  2054. private bool isTerminating;
  2055. private bool UpdateMainThreadActionsOnce(bool inDestroy)
  2056. {
  2057. if (!actionsForMainThread.TryTake(out (System.Action<bool> action, AutoResetEvent evt) item))
  2058. {
  2059. return false;
  2060. }
  2061. try
  2062. {
  2063. item.action(inDestroy);
  2064. }
  2065. catch (Exception ex)
  2066. {
  2067. Debug.LogError("Error in main thread lightning action: " + ex);
  2068. }
  2069. if (item.evt != null)
  2070. {
  2071. item.evt.Set();
  2072. autoResetEventPool.Add(item.evt);
  2073. }
  2074. return true;
  2075. }
  2076. private void BackgroundThreadMethod()
  2077. {
  2078. while (Running)
  2079. {
  2080. if (actionsForBackgroundThread.TryTake(out Action action, 500))
  2081. {
  2082. try
  2083. {
  2084. action();
  2085. }
  2086. catch (Exception ex)
  2087. {
  2088. actionsForMainThread.Add((inDestroy =>
  2089. {
  2090. Debug.LogError("Lightning background thread exception: " + ex);
  2091. }, null));
  2092. }
  2093. }
  2094. }
  2095. }
  2096. /// <summary>
  2097. /// Constructor - starts the thread
  2098. /// </summary>
  2099. /// <param name="multiThreaded">Multi-threaded?</param>
  2100. public LightningThreadState(bool multiThreaded)
  2101. {
  2102. this.mainThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId;
  2103. this.multiThreaded = multiThreaded;
  2104. #if TASK_AVAILABLE
  2105. lightningThread = Task.Factory.StartNew(BackgroundThreadMethod);
  2106. #else
  2107. lightningThread = new Thread(new ThreadStart(BackgroundThreadMethod))
  2108. {
  2109. IsBackground = true,
  2110. Name = "LightningBoltScriptThread"
  2111. };
  2112. lightningThread.Start();
  2113. #endif
  2114. }
  2115. /// <summary>
  2116. /// Terminate and wait for thread end
  2117. /// </summary>
  2118. /// <param name="inDestroy">True if in destroy, false otherwise</param>
  2119. public void TerminateAndWaitForEnd(bool inDestroy)
  2120. {
  2121. DateTime dt = DateTime.UtcNow;
  2122. TimeSpan timeout = TimeSpan.FromSeconds(5.0);
  2123. isTerminating = true;
  2124. while (UpdateMainThreadActionsOnce(inDestroy) || actionsForBackgroundThread.Count > 0)
  2125. {
  2126. Thread.Sleep(20);
  2127. if (DateTime.UtcNow - dt > timeout)
  2128. {
  2129. break;
  2130. }
  2131. }
  2132. }
  2133. /// <summary>
  2134. /// Execute any main thread actions from the main thread
  2135. /// </summary>
  2136. public void UpdateMainThreadActions()
  2137. {
  2138. if (multiThreaded)
  2139. {
  2140. while (UpdateMainThreadActionsOnce(false)) { }
  2141. }
  2142. }
  2143. /// <summary>
  2144. /// Add a main thread action
  2145. /// </summary>
  2146. /// <param name="action">Action</param>
  2147. /// <param name="waitForAction">True to wait for completion, false if not</param>
  2148. /// <returns>True if action added, false if in process of terminating the thread</returns>
  2149. public bool AddActionForMainThread(System.Action<bool> action, bool waitForAction = false)
  2150. {
  2151. if (isTerminating)
  2152. {
  2153. return false;
  2154. }
  2155. else if (System.Threading.Thread.CurrentThread.ManagedThreadId == mainThreadId ||
  2156. !multiThreaded)
  2157. {
  2158. action(true);
  2159. return true;
  2160. }
  2161. if (waitForAction)
  2162. {
  2163. if (!autoResetEventPool.TryTake(out AutoResetEvent evt))
  2164. {
  2165. evt = new AutoResetEvent(false);
  2166. }
  2167. actionsForMainThread.Add((action, evt));
  2168. evt.WaitOne(maxTimeoutWaitMainThread);
  2169. }
  2170. else
  2171. {
  2172. actionsForMainThread.Add((action, null));
  2173. }
  2174. return true;
  2175. }
  2176. /// <summary>
  2177. /// Add a background thread action
  2178. /// </summary>
  2179. /// <param name="action">Action</param>
  2180. /// <returns>True if action added, false if in process of terminating the thread</returns>
  2181. public bool AddActionForBackgroundThread(System.Action action)
  2182. {
  2183. if (isTerminating)
  2184. {
  2185. return false;
  2186. }
  2187. else if (!multiThreaded)
  2188. {
  2189. action();
  2190. }
  2191. else
  2192. {
  2193. actionsForBackgroundThread.Add(action);
  2194. }
  2195. return true;
  2196. }
  2197. }
  2198. #endif
  2199. }