VolumetricLightBeamEditor.cs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725
  1. //#define DEBUG_SHOW_CUSTOM_MATERIAL_INFO
  2. #if UNITY_EDITOR
  3. using UnityEngine;
  4. using UnityEditor;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. #pragma warning disable 0429, 0162 // Unreachable expression code detected (because of Noise3D.isSupported on mobile)
  8. namespace VLB
  9. {
  10. [CustomEditor(typeof(VolumetricLightBeam))]
  11. [CanEditMultipleObjects]
  12. public class VolumetricLightBeamEditor : EditorCommon
  13. {
  14. SerializedProperty trackChangesDuringPlaytime;
  15. SerializedProperty colorFromLight, colorMode, color, colorGradient;
  16. SerializedProperty intensityFromLight, intensityModeAdvanced, intensityInside, intensityOutside;
  17. SerializedProperty blendingMode, shaderAccuracy;
  18. SerializedProperty fresnelPow, glareFrontal, glareBehind;
  19. SerializedProperty spotAngleFromLight, spotAngle;
  20. SerializedProperty coneRadiusStart, geomMeshType, geomCustomSides, geomCustomSegments, geomCap;
  21. SerializedProperty fallOffEndFromLight, fallOffStart, fallOffEnd;
  22. SerializedProperty attenuationEquation, attenuationCustomBlending;
  23. SerializedProperty depthBlendDistance, cameraClippingDistance;
  24. SerializedProperty noiseMode, noiseIntensity, noiseScaleUseGlobal, noiseScaleLocal, noiseVelocityUseGlobal, noiseVelocityLocal;
  25. SerializedProperty fadeOutBegin, fadeOutEnd;
  26. SerializedProperty dimensions, sortingLayerID, sortingOrder;
  27. SerializedProperty skewingLocalForwardDirection, clippingPlaneTransform, tiltFactor;
  28. TargetList<VolumetricLightBeam> m_Targets;
  29. string[] m_SortingLayerNames;
  30. protected override void OnEnable()
  31. {
  32. base.OnEnable();
  33. m_Targets = new TargetList<VolumetricLightBeam>(targets);
  34. colorFromLight = FindProperty((VolumetricLightBeam x) => x.colorFromLight);
  35. color = FindProperty((VolumetricLightBeam x) => x.color);
  36. colorGradient = FindProperty((VolumetricLightBeam x) => x.colorGradient);
  37. colorMode = FindProperty((VolumetricLightBeam x) => x.colorMode);
  38. intensityFromLight = FindProperty((VolumetricLightBeam x) => x.intensityFromLight);
  39. intensityModeAdvanced = FindProperty((VolumetricLightBeam x) => x.intensityModeAdvanced);
  40. intensityInside = FindProperty((VolumetricLightBeam x) => x.intensityInside);
  41. intensityOutside = FindProperty((VolumetricLightBeam x) => x.intensityOutside);
  42. blendingMode = FindProperty((VolumetricLightBeam x) => x.blendingMode);
  43. shaderAccuracy = FindProperty((VolumetricLightBeam x) => x.shaderAccuracy);
  44. fresnelPow = FindProperty((VolumetricLightBeam x) => x.fresnelPow);
  45. glareFrontal = FindProperty((VolumetricLightBeam x) => x.glareFrontal);
  46. glareBehind = FindProperty((VolumetricLightBeam x) => x.glareBehind);
  47. spotAngleFromLight = FindProperty((VolumetricLightBeam x) => x.spotAngleFromLight);
  48. spotAngle = FindProperty((VolumetricLightBeam x) => x.spotAngle);
  49. coneRadiusStart = FindProperty((VolumetricLightBeam x) => x.coneRadiusStart);
  50. geomMeshType = FindProperty((VolumetricLightBeam x) => x.geomMeshType);
  51. geomCustomSides = FindProperty((VolumetricLightBeam x) => x.geomCustomSides);
  52. geomCustomSegments = FindProperty((VolumetricLightBeam x) => x.geomCustomSegments);
  53. geomCap = FindProperty((VolumetricLightBeam x) => x.geomCap);
  54. fallOffEndFromLight = FindProperty((VolumetricLightBeam x) => x.fallOffEndFromLight);
  55. fallOffStart = FindProperty((VolumetricLightBeam x) => x.fallOffStart);
  56. fallOffEnd = FindProperty((VolumetricLightBeam x) => x.fallOffEnd);
  57. attenuationEquation = FindProperty((VolumetricLightBeam x) => x.attenuationEquation);
  58. attenuationCustomBlending = FindProperty((VolumetricLightBeam x) => x.attenuationCustomBlending);
  59. depthBlendDistance = FindProperty((VolumetricLightBeam x) => x.depthBlendDistance);
  60. cameraClippingDistance = FindProperty((VolumetricLightBeam x) => x.cameraClippingDistance);
  61. // NOISE
  62. noiseMode = FindProperty((VolumetricLightBeam x) => x.noiseMode);
  63. noiseIntensity = FindProperty((VolumetricLightBeam x) => x.noiseIntensity);
  64. noiseScaleUseGlobal = FindProperty((VolumetricLightBeam x) => x.noiseScaleUseGlobal);
  65. noiseScaleLocal = FindProperty((VolumetricLightBeam x) => x.noiseScaleLocal);
  66. noiseVelocityUseGlobal = FindProperty((VolumetricLightBeam x) => x.noiseVelocityUseGlobal);
  67. noiseVelocityLocal = FindProperty((VolumetricLightBeam x) => x.noiseVelocityLocal);
  68. trackChangesDuringPlaytime = serializedObject.FindProperty("_TrackChangesDuringPlaytime");
  69. fadeOutBegin = serializedObject.FindProperty("_FadeOutBegin");
  70. fadeOutEnd = serializedObject.FindProperty("_FadeOutEnd");
  71. skewingLocalForwardDirection = FindProperty((VolumetricLightBeam x) => x.skewingLocalForwardDirection);
  72. clippingPlaneTransform = FindProperty((VolumetricLightBeam x) => x.clippingPlaneTransform);
  73. tiltFactor = FindProperty((VolumetricLightBeam x) => x.tiltFactor);
  74. // 2D
  75. sortingLayerID = serializedObject.FindProperty("_SortingLayerID");
  76. sortingOrder = serializedObject.FindProperty("_SortingOrder");
  77. dimensions = FindProperty((VolumetricLightBeam x) => x.dimensions);
  78. m_SortingLayerNames = SortingLayer.layers.Select(l => l.name).ToArray();
  79. }
  80. static void PropertyThickness(SerializedProperty sp)
  81. {
  82. sp.FloatSlider(
  83. EditorStrings.Beam.SideThickness,
  84. 0, 1,
  85. (value) => Mathf.Clamp01(1 - (value / Consts.FresnelPowMaxValue)), // conversion value to slider
  86. (value) => (1 - value) * Consts.FresnelPowMaxValue // conversion slider to value
  87. );
  88. }
  89. class ButtonToggleScope : System.IDisposable
  90. {
  91. SerializedProperty m_Property;
  92. bool m_DisableGroup = false;
  93. GUIContent m_Content = null;
  94. void Enable()
  95. {
  96. EditorGUILayout.BeginHorizontal();
  97. if(m_DisableGroup)
  98. EditorGUI.BeginDisabledGroup(m_Property.HasAtLeastOneValue(true));
  99. }
  100. void Disable()
  101. {
  102. if(m_DisableGroup)
  103. EditorGUI.EndDisabledGroup();
  104. DrawToggleButton();
  105. EditorGUILayout.EndHorizontal();
  106. }
  107. public ButtonToggleScope(SerializedProperty prop, bool disableGroup, GUIContent content)
  108. {
  109. m_Property = prop;
  110. m_DisableGroup = disableGroup;
  111. m_Content = content;
  112. Enable();
  113. }
  114. public void Dispose() { Disable(); }
  115. static GUIStyle ms_ToggleButtonStyleNormal = null;
  116. static GUIStyle ms_ToggleButtonStyleToggled = null;
  117. static GUIStyle ms_ToggleButtonStyleMixedValue = null;
  118. void DrawToggleButton()
  119. {
  120. if (ms_ToggleButtonStyleNormal == null)
  121. {
  122. ms_ToggleButtonStyleNormal = new GUIStyle(EditorStyles.miniButton);
  123. ms_ToggleButtonStyleToggled = new GUIStyle(ms_ToggleButtonStyleNormal);
  124. ms_ToggleButtonStyleToggled.normal.background = ms_ToggleButtonStyleToggled.active.background;
  125. ms_ToggleButtonStyleMixedValue = new GUIStyle(ms_ToggleButtonStyleToggled);
  126. ms_ToggleButtonStyleMixedValue.fontStyle = FontStyle.Italic;
  127. }
  128. EditorGUI.BeginChangeCheck();
  129. EditorGUI.showMixedValue = m_Property.hasMultipleDifferentValues;
  130. var style = EditorGUI.showMixedValue ? ms_ToggleButtonStyleMixedValue : (m_Property.boolValue ? ms_ToggleButtonStyleToggled : ms_ToggleButtonStyleNormal);
  131. var calcSize = style.CalcSize(m_Content);
  132. #if UNITY_2019_3_OR_NEWER
  133. var defaultColor = GUI.backgroundColor;
  134. if(m_Property.boolValue)
  135. GUI.backgroundColor = new Color(0.75f, 0.75f, 0.75f);
  136. #endif
  137. GUILayout.Button(
  138. m_Content,
  139. style,
  140. GUILayout.MaxWidth(calcSize.x));
  141. #if UNITY_2019_3_OR_NEWER
  142. GUI.backgroundColor = defaultColor;
  143. #endif
  144. EditorGUI.showMixedValue = false;
  145. if (EditorGUI.EndChangeCheck())
  146. m_Property.boolValue = !m_Property.boolValue;
  147. }
  148. }
  149. static ButtonToggleScope ButtonToggleScopeFromLight(SerializedProperty prop, bool visible)
  150. {
  151. if (!visible) return null;
  152. return new ButtonToggleScope(prop,
  153. true, // disableGroup
  154. EditorStrings.Beam.FromSpotLight);
  155. }
  156. static ButtonToggleScope ButtonToggleScopeAdvanced(SerializedProperty prop, bool visible)
  157. {
  158. if (!visible) return null;
  159. return new ButtonToggleScope(prop,
  160. false, // disableGroup
  161. EditorStrings.Beam.IntensityModeAdvanced);
  162. }
  163. public override void OnInspectorGUI()
  164. {
  165. base.OnInspectorGUI();
  166. Debug.Assert(m_Targets.Count > 0);
  167. #if DEBUG_SHOW_CUSTOM_MATERIAL_INFO
  168. if (m_Targets.Count == 1)
  169. {
  170. string msg = "";
  171. var geom = m_Targets[0].GetComponentInChildren<BeamGeometry>();
  172. if (geom == null)
  173. msg = "No BeamGeometry";
  174. else
  175. msg = geom._EDITOR_IsUsingCustomMaterial ? "Custom Material" : "GPU Instanced Shared Material";
  176. EditorGUILayout.HelpBox(msg, MessageType.Info);
  177. }
  178. #endif
  179. bool hasLightSpot = false;
  180. var light = m_Targets[0].GetComponent<Light>();
  181. if (light)
  182. {
  183. hasLightSpot = light.type == LightType.Spot;
  184. if (!hasLightSpot)
  185. {
  186. EditorGUILayout.HelpBox(EditorStrings.Beam.HelpNoSpotlight, MessageType.Warning);
  187. }
  188. }
  189. if (FoldableHeader.Begin(this, EditorStrings.Beam.HeaderBasic))
  190. {
  191. // Color
  192. using (ButtonToggleScopeFromLight(colorFromLight, hasLightSpot))
  193. {
  194. if (!hasLightSpot) EditorGUILayout.BeginHorizontal(); // mandatory to have the color picker on the same line (when the button "from light" is not here)
  195. {
  196. if (Config.Instance.featureEnabledColorGradient == FeatureEnabledColorGradient.Off)
  197. {
  198. EditorGUILayout.PropertyField(color, EditorStrings.Beam.ColorMode);
  199. }
  200. else
  201. {
  202. EditorGUIUtility.fieldWidth = 65.0f;
  203. EditorGUILayout.PropertyField(colorMode, EditorStrings.Beam.ColorMode);
  204. EditorGUIUtility.fieldWidth = 0.0f;
  205. if (colorMode.enumValueIndex == (int)ColorMode.Gradient)
  206. EditorGUILayout.PropertyField(colorGradient, EditorStrings.Beam.ColorGradient);
  207. else
  208. EditorGUILayout.PropertyField(color, EditorStrings.Beam.ColorFlat);
  209. }
  210. }
  211. if (!hasLightSpot) EditorGUILayout.EndHorizontal();
  212. }
  213. // Blending Mode
  214. EditorGUILayout.PropertyField(blendingMode, EditorStrings.Beam.BlendingMode);
  215. EditorGUILayout.Separator();
  216. // Intensity
  217. bool advancedModeEnabled = false;
  218. using (ButtonToggleScopeFromLight(intensityFromLight, hasLightSpot))
  219. {
  220. bool advancedModeButton = !hasLightSpot || intensityFromLight.HasAtLeastOneValue(false);
  221. using (ButtonToggleScopeAdvanced(intensityModeAdvanced, advancedModeButton))
  222. {
  223. advancedModeEnabled = intensityModeAdvanced.HasAtLeastOneValue(true);
  224. EditorGUILayout.PropertyField(intensityOutside, advancedModeEnabled ? EditorStrings.Beam.IntensityOutside : EditorStrings.Beam.IntensityGlobal);
  225. }
  226. }
  227. if (advancedModeEnabled)
  228. EditorGUILayout.PropertyField(intensityInside, EditorStrings.Beam.IntensityInside);
  229. else
  230. intensityInside.floatValue = intensityOutside.floatValue;
  231. EditorGUILayout.Separator();
  232. // Spot Angle
  233. using (ButtonToggleScopeFromLight(spotAngleFromLight, hasLightSpot))
  234. {
  235. EditorGUILayout.PropertyField(spotAngle, EditorStrings.Beam.SpotAngle);
  236. }
  237. PropertyThickness(fresnelPow);
  238. EditorGUILayout.Separator();
  239. EditorGUILayout.PropertyField(glareFrontal, EditorStrings.Beam.GlareFrontal);
  240. EditorGUILayout.PropertyField(glareBehind, EditorStrings.Beam.GlareBehind);
  241. EditorGUILayout.Separator();
  242. if (Config.Instance.featureEnabledShaderAccuracyHigh)
  243. {
  244. EditorGUILayout.PropertyField(shaderAccuracy, EditorStrings.Beam.ShaderAccuracy);
  245. EditorGUILayout.Separator();
  246. }
  247. trackChangesDuringPlaytime.ToggleLeft(EditorStrings.Beam.TrackChanges);
  248. DrawAnimatorWarning();
  249. }
  250. FoldableHeader.End();
  251. if(FoldableHeader.Begin(this, EditorStrings.Beam.HeaderAttenuation))
  252. {
  253. EditorGUILayout.BeginHorizontal();
  254. {
  255. EditorGUILayout.PropertyField(attenuationEquation, EditorStrings.Beam.AttenuationEquation);
  256. if (attenuationEquation.enumValueIndex == (int)AttenuationEquation.Blend)
  257. EditorGUILayout.PropertyField(attenuationCustomBlending, EditorStrings.Beam.AttenuationCustomBlending);
  258. }
  259. EditorGUILayout.EndHorizontal();
  260. // Fade End
  261. using (ButtonToggleScopeFromLight(fallOffEndFromLight, hasLightSpot))
  262. {
  263. EditorGUILayout.PropertyField(fallOffEnd, EditorStrings.Beam.FallOffEnd);
  264. }
  265. if (fallOffEnd.hasMultipleDifferentValues)
  266. EditorGUILayout.PropertyField(fallOffStart, EditorStrings.Beam.FallOffStart);
  267. else
  268. fallOffStart.FloatSlider(EditorStrings.Beam.FallOffStart, 0f, fallOffEnd.floatValue - Consts.FallOffDistancesMinThreshold);
  269. EditorGUILayout.Separator();
  270. // Tilt
  271. EditorGUILayout.PropertyField(tiltFactor, EditorStrings.Beam.TiltFactor);
  272. GlobalToggle(ref VolumetricLightBeam.editorShowTiltFactor, EditorStrings.Beam.EditorShowTiltDirection, "VLB_BEAM_SHOWTILTDIR");
  273. if (m_Targets.HasAtLeastOneTargetWith((VolumetricLightBeam beam) => { return beam.isTilted && beam.shaderAccuracy != ShaderAccuracy.High; }))
  274. EditorGUILayout.HelpBox(EditorStrings.Beam.HelpTiltedWithShaderAccuracyFast, MessageType.Warning);
  275. }
  276. FoldableHeader.End();
  277. if (Config.Instance.featureEnabledNoise3D)
  278. {
  279. if (FoldableHeader.Begin(this, EditorStrings.Beam.Header3DNoise))
  280. {
  281. noiseMode.CustomEnum<NoiseMode>(EditorStrings.Beam.NoiseMode, EditorStrings.Beam.NoiseModeEnumDescriptions);
  282. bool showNoiseProps = m_Targets.HasAtLeastOneTargetWith((VolumetricLightBeam beam) => { return beam.isNoiseEnabled; });
  283. if (showNoiseProps)
  284. {
  285. EditorGUILayout.PropertyField(noiseIntensity, EditorStrings.Beam.NoiseIntensity);
  286. using (new EditorGUILayout.HorizontalScope())
  287. {
  288. using (new EditorGUI.DisabledGroupScope(noiseScaleUseGlobal.boolValue))
  289. {
  290. EditorGUILayout.PropertyField(noiseScaleLocal, EditorStrings.Beam.NoiseScale);
  291. }
  292. noiseScaleUseGlobal.ToggleUseGlobalNoise();
  293. }
  294. using (new EditorGUILayout.HorizontalScope())
  295. {
  296. using (new EditorGUI.DisabledGroupScope(noiseVelocityUseGlobal.boolValue))
  297. {
  298. EditorGUILayout.PropertyField(noiseVelocityLocal, EditorStrings.Beam.NoiseVelocity);
  299. }
  300. noiseVelocityUseGlobal.ToggleUseGlobalNoise();
  301. }
  302. ButtonOpenConfig();
  303. if (Noise3D.isSupported && !Noise3D.isProperlyLoaded)
  304. EditorGUILayout.HelpBox(EditorStrings.Common.HelpNoiseLoadingFailed, MessageType.Error);
  305. if (!Noise3D.isSupported)
  306. EditorGUILayout.HelpBox(Noise3D.isNotSupportedString, MessageType.Info);
  307. }
  308. }
  309. FoldableHeader.End();
  310. }
  311. if(FoldableHeader.Begin(this, EditorStrings.Beam.HeaderBlendingDistances))
  312. {
  313. {
  314. var content = AddEnabledStatusToContentText(EditorStrings.Beam.CameraClippingDistance, cameraClippingDistance);
  315. EditorGUILayout.PropertyField(cameraClippingDistance, content);
  316. }
  317. {
  318. var content = AddEnabledStatusToContentText(EditorStrings.Beam.DepthBlendDistance, depthBlendDistance);
  319. EditorGUILayout.PropertyField(depthBlendDistance, content);
  320. }
  321. }
  322. FoldableHeader.End();
  323. if(FoldableHeader.Begin(this, EditorStrings.Beam.HeaderGeometry))
  324. {
  325. using (new EditorGUILayout.HorizontalScope())
  326. {
  327. EditorGUILayout.PropertyField(coneRadiusStart, EditorStrings.Beam.ConeRadiusStart);
  328. EditorGUI.BeginChangeCheck();
  329. {
  330. geomCap.ToggleLeft(EditorStrings.Beam.GeomCap, GUILayout.MaxWidth(40.0f));
  331. }
  332. if (EditorGUI.EndChangeCheck()) { SetMeshesDirty(); }
  333. }
  334. EditorGUI.BeginChangeCheck();
  335. {
  336. EditorGUILayout.PropertyField(geomMeshType, EditorStrings.Beam.GeomMeshType);
  337. }
  338. if (EditorGUI.EndChangeCheck()) { SetMeshesDirty(); }
  339. if (geomMeshType.intValue == (int)MeshType.Custom)
  340. {
  341. EditorGUI.indentLevel++;
  342. EditorGUI.BeginChangeCheck();
  343. {
  344. EditorGUILayout.PropertyField(geomCustomSides, EditorStrings.Beam.GeomSides);
  345. EditorGUILayout.PropertyField(geomCustomSegments, EditorStrings.Beam.GeomSegments);
  346. }
  347. if (EditorGUI.EndChangeCheck()) { SetMeshesDirty(); }
  348. if (Config.Instance.featureEnabledMeshSkewing)
  349. {
  350. var vec3 = skewingLocalForwardDirection.vector3Value;
  351. var vec2 = Vector2.zero;
  352. EditorGUI.BeginChangeCheck();
  353. {
  354. vec2 = EditorGUILayout.Vector2Field(EditorStrings.Beam.SkewingLocalForwardDirection, vec3.xy());
  355. }
  356. if (EditorGUI.EndChangeCheck())
  357. {
  358. vec3 = new Vector3(vec2.x, vec2.y, 1.0f);
  359. skewingLocalForwardDirection.vector3Value = vec3;
  360. SetMeshesDirty();
  361. }
  362. }
  363. EditorGUI.indentLevel--;
  364. }
  365. if (m_Targets.Count == 1)
  366. {
  367. EditorGUILayout.HelpBox(m_Targets[0].meshStats, MessageType.Info);
  368. }
  369. EditorGUILayout.Separator();
  370. EditorGUILayout.PropertyField(clippingPlaneTransform, EditorStrings.Beam.ClippingPlane);
  371. if (m_Targets.HasAtLeastOneTargetWith((VolumetricLightBeam beam) => { return beam.clippingPlaneTransform != null; }))
  372. {
  373. GlobalToggle(ref VolumetricLightBeam.editorShowClippingPlane, EditorStrings.Beam.EditorShowClippingPlane, "VLB_BEAM_SHOWADDCLIPPINGPLANE");
  374. }
  375. }
  376. FoldableHeader.End();
  377. if (FoldableHeader.Begin(this, EditorStrings.Beam.HeaderFadeOut))
  378. {
  379. bool wasEnabled = fadeOutBegin.floatValue <= fadeOutEnd.floatValue;
  380. if(m_Targets.HasAtLeastOneTargetWith((VolumetricLightBeam beam) => { return (beam.fadeOutBegin <= beam.fadeOutEnd) != wasEnabled; }))
  381. {
  382. wasEnabled = true;
  383. EditorGUI.showMixedValue = true;
  384. }
  385. System.Action<float> setFadeOutBegin = value =>
  386. {
  387. fadeOutBegin.floatValue = value;
  388. m_Targets.RecordUndoAction("Change Fade Out Begin Distance",
  389. (VolumetricLightBeam beam) => { beam.fadeOutBegin = value; });
  390. };
  391. System.Action<float> setFadeOutEnd = value =>
  392. {
  393. fadeOutEnd.floatValue = value;
  394. m_Targets.RecordUndoAction("Change Fade Out End Distance",
  395. (VolumetricLightBeam beam) => { beam.fadeOutEnd = value; });
  396. };
  397. EditorGUI.BeginChangeCheck();
  398. bool isEnabled = EditorGUILayout.Toggle(EditorStrings.Beam.FadeOutEnabled, wasEnabled);
  399. EditorGUI.showMixedValue = false;
  400. if (EditorGUI.EndChangeCheck())
  401. {
  402. float invValue = isEnabled ? 1 : -1;
  403. float valueA = Mathf.Abs(fadeOutBegin.floatValue);
  404. float valueB = Mathf.Abs(fadeOutEnd.floatValue);
  405. setFadeOutBegin(invValue * Mathf.Min(valueA, valueB));
  406. setFadeOutEnd (invValue * Mathf.Max(valueA, valueB));
  407. }
  408. if (isEnabled)
  409. {
  410. const float kEpsilon = 0.1f;
  411. EditorGUI.BeginChangeCheck();
  412. EditorGUILayout.PropertyField(fadeOutBegin, EditorStrings.Beam.FadeOutBegin);
  413. if(EditorGUI.EndChangeCheck())
  414. {
  415. setFadeOutBegin(Mathf.Clamp(fadeOutBegin.floatValue, 0, fadeOutEnd.floatValue - kEpsilon));
  416. }
  417. EditorGUI.BeginChangeCheck();
  418. EditorGUILayout.PropertyField(fadeOutEnd, EditorStrings.Beam.FadeOutEnd);
  419. if(EditorGUI.EndChangeCheck())
  420. {
  421. setFadeOutEnd(Mathf.Max(fadeOutBegin.floatValue + kEpsilon, fadeOutEnd.floatValue));
  422. }
  423. if (Application.isPlaying)
  424. {
  425. if(Config.Instance.fadeOutCameraTransform == null)
  426. {
  427. EditorGUILayout.HelpBox(EditorStrings.Beam.HelpFadeOutNoMainCamera, MessageType.Error);
  428. }
  429. }
  430. }
  431. }
  432. FoldableHeader.End();
  433. if (FoldableHeader.Begin(this, EditorStrings.Beam.Header2D))
  434. {
  435. dimensions.CustomEnum<Dimensions>(EditorStrings.Beam.Dimensions, EditorStrings.Common.DimensionsEnumDescriptions);
  436. DrawSortingLayer();
  437. DrawSortingOrder();
  438. }
  439. FoldableHeader.End();
  440. if (DrawInfos())
  441. {
  442. DrawLineSeparator();
  443. }
  444. DrawCustomActionButtons();
  445. DrawAdditionalFeatures();
  446. serializedObject.ApplyModifiedProperties();
  447. }
  448. GUIContent AddEnabledStatusToContentText(GUIContent inContent, SerializedProperty prop)
  449. {
  450. Debug.Assert(prop.propertyType == SerializedPropertyType.Float);
  451. var content = new GUIContent(inContent);
  452. if (prop.hasMultipleDifferentValues)
  453. content.text += " (-)";
  454. else
  455. content.text += prop.floatValue > 0.0 ? " (on)" : " (off)";
  456. return content;
  457. }
  458. void SetMeshesDirty()
  459. {
  460. foreach (var entity in m_Targets) entity._EditorSetMeshDirty();
  461. }
  462. void DrawSortingLayer()
  463. {
  464. EditorGUI.BeginChangeCheck();
  465. EditorGUI.showMixedValue = sortingLayerID.hasMultipleDifferentValues;
  466. int layerIndex = System.Array.IndexOf(m_SortingLayerNames, SortingLayer.IDToName(sortingLayerID.intValue));
  467. layerIndex = EditorGUILayout.Popup(EditorStrings.Beam.SortingLayer, layerIndex, m_SortingLayerNames);
  468. EditorGUI.showMixedValue = false;
  469. if (EditorGUI.EndChangeCheck())
  470. {
  471. sortingLayerID.intValue = SortingLayer.NameToID(m_SortingLayerNames[layerIndex]);
  472. m_Targets.RecordUndoAction("Edit Sorting Layer",
  473. (VolumetricLightBeam beam) => beam.sortingLayerID = sortingLayerID.intValue ); // call setters
  474. }
  475. }
  476. void DrawSortingOrder()
  477. {
  478. EditorGUI.BeginChangeCheck();
  479. EditorGUILayout.PropertyField(sortingOrder, EditorStrings.Beam.SortingOrder);
  480. if (EditorGUI.EndChangeCheck())
  481. {
  482. m_Targets.RecordUndoAction("Edit Sorting Order",
  483. (VolumetricLightBeam beam) => beam.sortingOrder = sortingOrder.intValue ); // call setters
  484. }
  485. }
  486. void DrawAnimatorWarning()
  487. {
  488. var showAnimatorWarning = m_Targets.HasAtLeastOneTargetWith((VolumetricLightBeam beam) => { return beam.GetComponent<Animator>() != null && beam.trackChangesDuringPlaytime == false; });
  489. if (showAnimatorWarning)
  490. EditorGUILayout.HelpBox(EditorStrings.Beam.HelpAnimatorWarning, MessageType.Warning);
  491. }
  492. void DrawCustomActionButtons()
  493. {
  494. using (new EditorGUILayout.HorizontalScope())
  495. {
  496. if (GUILayout.Button(EditorStrings.Beam.ButtonResetProperties, EditorStyles.miniButtonLeft))
  497. {
  498. m_Targets.RecordUndoAction("Reset Light Beam Properties",
  499. (VolumetricLightBeam beam) => { beam.Reset(); beam.GenerateGeometry(); } );
  500. }
  501. if (geomMeshType.intValue == (int)MeshType.Custom)
  502. {
  503. if (GUILayout.Button(EditorStrings.Beam.ButtonGenerateGeometry, EditorStyles.miniButtonRight))
  504. {
  505. foreach (var entity in m_Targets) entity.GenerateGeometry();
  506. }
  507. }
  508. }
  509. }
  510. void AddComponentToTargets<T>() where T : MonoBehaviour
  511. {
  512. foreach (var target in m_Targets) EditorExtensions.AddComponentFromEditor<T>(target);
  513. }
  514. void DrawAdditionalFeatures()
  515. {
  516. #if UNITY_5_5_OR_NEWER
  517. bool showButtonDust = m_Targets.HasAtLeastOneTargetWith((VolumetricLightBeam beam) => { return beam.GetComponent<VolumetricDustParticles>() == null; });
  518. #else
  519. bool showButtonDust = false;
  520. #endif
  521. bool showButtonOcclusion = Config.Instance.featureEnabledDynamicOcclusion && m_Targets.HasAtLeastOneTargetWith((VolumetricLightBeam beam) => { return beam.GetComponent<DynamicOcclusionAbstractBase>() == null; });
  522. bool showButtonTriggerZone = m_Targets.HasAtLeastOneTargetWith((VolumetricLightBeam beam) => { return beam.GetComponent<TriggerZone>() == null; });
  523. if (showButtonDust || showButtonOcclusion || showButtonTriggerZone)
  524. {
  525. using (new EditorGUILayout.HorizontalScope())
  526. {
  527. if (showButtonDust && GUILayout.Button(EditorStrings.Beam.ButtonAddDustParticles, EditorStyles.miniButton))
  528. {
  529. AddComponentToTargets<VolumetricDustParticles>();
  530. }
  531. if (showButtonOcclusion && GUILayout.Button(EditorStrings.Beam.ButtonAddDynamicOcclusion, EditorStyles.miniButton))
  532. {
  533. var menu = new GenericMenu();
  534. menu.AddItem(new GUIContent("+ Dynamic Occlusion (Raycasting)"), false, AddComponentToTargets<DynamicOcclusionRaycasting>);
  535. menu.AddItem(new GUIContent("+ Dynamic Occlusion (Depth Texture)"), false, AddComponentToTargets<DynamicOcclusionDepthBuffer>);
  536. menu.ShowAsContext();
  537. }
  538. if (showButtonTriggerZone && GUILayout.Button(EditorStrings.Beam.ButtonAddTriggerZone, EditorStyles.miniButton))
  539. {
  540. AddComponentToTargets<TriggerZone>();
  541. }
  542. }
  543. }
  544. }
  545. struct InfoTip
  546. {
  547. public MessageType type;
  548. public string message;
  549. }
  550. bool DrawInfos()
  551. {
  552. var tips = GetInfoTips();
  553. var gpuInstancingReport = GetBatchingReport();
  554. if (tips.Count > 0 || !string.IsNullOrEmpty(gpuInstancingReport))
  555. {
  556. if (FoldableHeader.Begin(this, EditorStrings.Beam.HeaderInfos))
  557. {
  558. foreach (var tip in tips)
  559. EditorGUILayout.HelpBox(tip.message, tip.type);
  560. if (!string.IsNullOrEmpty(gpuInstancingReport))
  561. EditorGUILayout.HelpBox(gpuInstancingReport, MessageType.Warning);
  562. }
  563. FoldableHeader.End();
  564. return true;
  565. }
  566. return false;
  567. }
  568. List<InfoTip> GetInfoTips()
  569. {
  570. var tips = new List<InfoTip>();
  571. if (m_Targets.Count == 1)
  572. {
  573. if (depthBlendDistance.floatValue > 0f || !Noise3D.isSupported || trackChangesDuringPlaytime.boolValue)
  574. {
  575. if (depthBlendDistance.floatValue > 0f)
  576. {
  577. tips.Add(new InfoTip { type = MessageType.Info, message = EditorStrings.Beam.HelpDepthTextureMode });
  578. #if UNITY_IPHONE || UNITY_IOS || UNITY_ANDROID
  579. tips.Add(new InfoTip { type = MessageType.Info, message = EditorStrings.Beam.HelpDepthMobile });
  580. #endif
  581. }
  582. if (trackChangesDuringPlaytime.boolValue)
  583. tips.Add(new InfoTip { type = MessageType.Info, message = EditorStrings.Beam.HelpTrackChangesEnabled });
  584. }
  585. }
  586. return tips;
  587. }
  588. string GetBatchingReport()
  589. {
  590. if (m_Targets.Count > 1)
  591. {
  592. string reasons = "";
  593. for (int i = 1; i < m_Targets.Count; ++i)
  594. {
  595. if (!BatchingHelper.CanBeBatched(m_Targets[0], m_Targets[i], ref reasons))
  596. {
  597. return "Selected beams can't be batched together:\n" + reasons;
  598. }
  599. }
  600. }
  601. return null;
  602. }
  603. }
  604. }
  605. #endif