No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Light2DEditor.cs 45KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889
  1. using System.Collections.Generic;
  2. using System.Linq;
  3. using UnityEditor.EditorTools;
  4. using UnityEditor.Rendering.Universal.Path2D;
  5. using UnityEngine;
  6. using UnityEngine.Rendering.Universal;
  7. namespace UnityEditor.Rendering.Universal
  8. {
  9. [CustomEditor(typeof(Light2D))]
  10. [CanEditMultipleObjects]
  11. internal class Light2DEditor : PathComponentEditor<ScriptablePath>
  12. {
  13. [EditorTool("Edit Freeform Shape", typeof(Light2D))]
  14. class FreeformShapeTool : PathEditorTool<ScriptablePath>
  15. {
  16. const string k_ShapePath = "m_ShapePath";
  17. public override bool IsAvailable()
  18. {
  19. var light = target as Light2D;
  20. if (light == null)
  21. return false;
  22. else
  23. return base.IsAvailable() && light.lightType == Light2D.LightType.Freeform;
  24. }
  25. protected override IShape GetShape(Object target)
  26. {
  27. return (target as Light2D).shapePath.ToPolygon(false);
  28. }
  29. protected override void SetShape(ScriptablePath shapeEditor, SerializedObject serializedObject)
  30. {
  31. serializedObject.Update();
  32. var pointsProperty = serializedObject.FindProperty(k_ShapePath);
  33. pointsProperty.arraySize = shapeEditor.pointCount;
  34. for (var i = 0; i < shapeEditor.pointCount; ++i)
  35. pointsProperty.GetArrayElementAtIndex(i).vector3Value = shapeEditor.GetPoint(i).position;
  36. ((Light2D)(serializedObject.targetObject)).UpdateMesh();
  37. // This is untracked right now...
  38. serializedObject.ApplyModifiedProperties();
  39. }
  40. }
  41. private struct ToggleFoldoutResult
  42. {
  43. public bool foldoutState;
  44. public bool toggleState;
  45. }
  46. private static class Styles
  47. {
  48. public static readonly GUIContent InnerOuterSpotAngle = EditorGUIUtility.TrTextContent("Inner / Outer Spot Angle", "Adjusts the inner / outer angles of this light to change the angle ranges of this Spot Light’s beam.");
  49. public static Texture lightCapTopRight = Resources.Load<Texture>("LightCapTopRight");
  50. public static Texture lightCapTopLeft = Resources.Load<Texture>("LightCapTopLeft");
  51. public static Texture lightCapBottomLeft = Resources.Load<Texture>("LightCapBottomLeft");
  52. public static Texture lightCapBottomRight = Resources.Load<Texture>("LightCapBottomRight");
  53. public static Texture lightCapUp = Resources.Load<Texture>("LightCapUp");
  54. public static Texture lightCapDown = Resources.Load<Texture>("LightCapDown");
  55. public static GUIContent lightTypeFreeform = new GUIContent("Freeform", Resources.Load("InspectorIcons/FreeformLight") as Texture);
  56. public static GUIContent lightTypeSprite = new GUIContent("Sprite", Resources.Load("InspectorIcons/SpriteLight") as Texture);
  57. public static GUIContent lightTypePoint = new GUIContent("Spot", Resources.Load("InspectorIcons/PointLight") as Texture);
  58. public static GUIContent lightTypeGlobal = new GUIContent("Global", Resources.Load("InspectorIcons/GlobalLight") as Texture);
  59. public static GUIContent[] lightTypeOptions = new GUIContent[] { lightTypeFreeform, lightTypeSprite, lightTypePoint, lightTypeGlobal };
  60. public static GUIContent blendingSettingsFoldout = EditorGUIUtility.TrTextContent("Blending", "Options used for blending");
  61. public static GUIContent shadowsSettingsFoldout = EditorGUIUtility.TrTextContent("Shadows", "Options used for shadows");
  62. public static GUIContent volumetricSettingsFoldout = EditorGUIUtility.TrTextContent("Volumetric", "Options used for volumetric lighting");
  63. public static GUIContent normalMapsSettingsFoldout = EditorGUIUtility.TrTextContent("Normal Maps", "Options used for normal maps");
  64. public static GUIContent generalLightType = EditorGUIUtility.TrTextContent("Light Type", "Select the light type. \n\nGlobal Light: For ambient light. \nSpot Light: For a spot light / point light. \nFreeform Light: For a custom shape light. \nSprite Light: For a custom light cookie using Sprites.");
  65. public static GUIContent generalFalloffSize = EditorGUIUtility.TrTextContent("Falloff", "Adjusts the falloff area of this light. The higher the falloff value, the larger area the falloff spans.");
  66. public static GUIContent generalFalloffIntensity = EditorGUIUtility.TrTextContent("Falloff Strength", "Adjusts the falloff curve to control the softness of this light’s edges. The higher the falloff strength, the softer the edges of this light.");
  67. public static GUIContent generalLightColor = EditorGUIUtility.TrTextContent("Color", "Adjusts this light’s color.");
  68. public static GUIContent generalLightIntensity = EditorGUIUtility.TrTextContent("Intensity", "Adjusts this light’s color intensity by using multiply to brighten the Sprite beyond its original color.");
  69. public static GUIContent generalVolumeIntensity = EditorGUIUtility.TrTextContent("Intensity", "Adjusts the intensity of this additional light volume that's additively blended on top of this light. To enable the Volumetric Shadow Strength, increase this Intensity to be greater than 0.");
  70. public static GUIContent generalBlendStyle = EditorGUIUtility.TrTextContent("Blend Style", "Adjusts how this light blends with the Sprites on the Target Sorting Layers. Different Blend Styles can be customized in the 2D Renderer Data Asset.");
  71. public static GUIContent generalLightOverlapOperation = EditorGUIUtility.TrTextContent("Overlap Operation", "Determines how this light blends with the other lights either through additive or alpha blending.");
  72. public static GUIContent generalLightOrder = EditorGUIUtility.TrTextContent("Light Order", "Determines the relative order in which lights of the same Blend Style get rendered. Lights with lower values are rendered first.");
  73. public static GUIContent generalShadowIntensity = EditorGUIUtility.TrTextContent("Strength", "Adjusts the amount of light occlusion from the Shadow Caster 2D component(s) when blocking this light.The higher the value, the more opaque the shadow becomes.");
  74. public static GUIContent generalShadowSoftness = EditorGUIUtility.TrTextContent("Softness", "Adjusts the amount of softness at the edge of the shadow.");
  75. public static GUIContent generalShadowSoftnessFalloffIntensity = EditorGUIUtility.TrTextContent("Falloff Strength", "Adjusts the falloff curve to control the softness of the shadow edges. The higher the falloff strength, the softer the edges of this shadow.");
  76. public static GUIContent generalShadowVolumeIntensity = EditorGUIUtility.TrTextContent("Shadow Strength", "Adjusts the amount of volume light occlusion from the Shadow Caster 2D component(s) when blocking this light.");
  77. public static GUIContent generalSortingLayerPrefixLabel = EditorGUIUtility.TrTextContent("Target Sorting Layers", "Determines which layers this light affects. To optimize performance, minimize the number of layers this light affects.");
  78. public static GUIContent generalLightNoLightEnabled = EditorGUIUtility.TrTextContentWithIcon("No valid blend styles are enabled.", MessageType.Error);
  79. public static GUIContent generalNormalMapZDistance = EditorGUIUtility.TrTextContent("Distance", "Adjusts the z-axis distance of this light and the lit Sprite(s). Do note that this distance does not Transform the position of this light in the Scene.");
  80. public static GUIContent generalNormalMapLightQuality = EditorGUIUtility.TrTextContent("Quality", "Determines the accuracy of the lighting calculations when normal map is used. To optimize for performance, select Fast.");
  81. public static GUIContent pointLightRadius = EditorGUIUtility.TrTextContent("Radius", "Adjusts the inner / outer radius of this light to change the size of this light.");
  82. public static GUIContent pointLightInner = EditorGUIUtility.TrTextContent("Inner", "Specify the inner radius of the light");
  83. public static GUIContent pointLightOuter = EditorGUIUtility.TrTextContent("Outer", "Specify the outer radius of the light");
  84. public static GUIContent pointLightSprite = EditorGUIUtility.TrTextContent("Sprite", "Specify the sprite (deprecated)");
  85. public static GUIContent shapeLightSprite = EditorGUIUtility.TrTextContent("Sprite", "Assign a Sprite which acts as a mask to create a light cookie.");
  86. public static GUIContent deprecatedParametricLightWarningSingle = EditorGUIUtility.TrTextContentWithIcon("Parametic Lights have been deprecated. To continue, upgrade your Parametric Light to a Freeform Light to enjoy similar light functionality.", MessageType.Warning);
  87. public static GUIContent deprecatedParametricLightWarningMulti = EditorGUIUtility.TrTextContentWithIcon("Parametic Lights have been deprecated. To continue, upgrade your Parametric Lights to Freeform Lights to enjoy similar light functionality.", MessageType.Warning);
  88. public static GUIContent deprecatedParametricLightInstructions = EditorGUIUtility.TrTextContent("Alternatively, you may choose to upgrade from the menu. Window > Rendering > Render Pipeline Converter > URP 2D Converters");
  89. public static GUIContent deprecatedParametricLightButtonSingle = EditorGUIUtility.TrTextContent("Upgrade Parametric Light");
  90. public static GUIContent deprecatedParametricLightButtonMulti = EditorGUIUtility.TrTextContent("Upgrade Parametric Lights");
  91. public static GUIContent renderPipelineUnassignedWarning = EditorGUIUtility.TrTextContentWithIcon("Universal scriptable renderpipeline asset must be assigned in Graphics Settings or Quality Settings.", MessageType.Warning);
  92. public static GUIContent asset2DUnassignedWarning = EditorGUIUtility.TrTextContentWithIcon("2D renderer data must be assigned to your universal render pipeline asset or camera.", MessageType.Warning);
  93. public static string deprecatedParametricLightDialogTextSingle = "The upgrade will convert the selected parametric light into a freeform light. You can't undo this operation.";
  94. public static string deprecatedParametricLightDialogTextMulti = "The upgrade will convert the selected parametric lights into freeform lights. You can't undo this operation.";
  95. public static string deprecatedParametricLightDialogTitle = "Parametric Light Upgrader";
  96. public static string deprecatedParametricLightDialogProceed = "Proceed";
  97. public static string deprecatedParametricLightDialogCancel = "Cancel";
  98. }
  99. const float k_GlobalLightGizmoSize = 1.2f;
  100. const float k_AngleCapSize = 0.16f * k_GlobalLightGizmoSize;
  101. const float k_AngleCapOffset = 0.08f * k_GlobalLightGizmoSize;
  102. const float k_AngleCapOffsetSecondary = -0.05f;
  103. const float k_RangeCapSize = 0.025f * k_GlobalLightGizmoSize;
  104. const float k_InnerRangeCapSize = 0.08f * k_GlobalLightGizmoSize;
  105. SerializedProperty m_LightType;
  106. SerializedProperty m_LightColor;
  107. SerializedProperty m_LightIntensity;
  108. SerializedProperty m_UseNormalMap;
  109. SerializedProperty m_ShadowsEnabled;
  110. SerializedProperty m_ShadowIntensity;
  111. SerializedProperty m_ShadowSoftness;
  112. SerializedProperty m_ShadowSoftnessFalloffIntensity;
  113. SerializedProperty m_ShadowVolumeIntensity;
  114. SerializedProperty m_ShadowVolumeIntensityEnabled;
  115. SerializedProperty m_ApplyToSortingLayers;
  116. SerializedProperty m_VolumetricIntensity;
  117. SerializedProperty m_VolumetricEnabled;
  118. SerializedProperty m_BlendStyleIndex;
  119. SerializedProperty m_FalloffIntensity;
  120. SerializedProperty m_NormalMapZDistance;
  121. SerializedProperty m_NormalMapQuality;
  122. SerializedProperty m_LightOrder;
  123. SerializedProperty m_OverlapOperation;
  124. // Point Light Properties
  125. SerializedProperty m_PointInnerAngle;
  126. SerializedProperty m_PointOuterAngle;
  127. SerializedProperty m_PointInnerRadius;
  128. SerializedProperty m_PointOuterRadius;
  129. SerializedProperty m_DeprecatedPointLightSprite;
  130. // Shape Light Properties
  131. SerializedProperty m_ShapeLightParametricRadius;
  132. SerializedProperty m_ShapeLightFalloffSize;
  133. SerializedProperty m_ShapeLightParametricSides;
  134. SerializedProperty m_ShapeLightSprite;
  135. SavedBool m_BlendingSettingsFoldout;
  136. SavedBool m_ShadowsSettingsFoldout;
  137. SavedBool m_VolumetricSettingsFoldout;
  138. SavedBool m_NormalMapsSettingsFoldout;
  139. int[] m_BlendStyleIndices;
  140. GUIContent[] m_BlendStyleNames;
  141. bool m_AnyBlendStyleEnabled = false;
  142. SortingLayerDropDown m_SortingLayerDropDown;
  143. Light2D lightObject => target as Light2D;
  144. Analytics.Renderer2DAnalytics m_Analytics;
  145. HashSet<Light2D> m_ModifiedLights;
  146. private void AnalyticsTrackChanges(SerializedObject serializedObject)
  147. {
  148. if (serializedObject.hasModifiedProperties)
  149. {
  150. foreach (Object targetObj in serializedObject.targetObjects)
  151. {
  152. Light2D light2d = (Light2D)targetObj;
  153. if (!m_ModifiedLights.Contains(light2d))
  154. m_ModifiedLights.Add(light2d);
  155. }
  156. }
  157. }
  158. private ToggleFoldoutResult DrawHeaderFoldoutWithToggle(GUIContent title, bool foldoutState, bool toggleState, string documentationURL = "")
  159. {
  160. ToggleFoldoutResult foldoutResult = new ToggleFoldoutResult();
  161. const float height = 17f;
  162. var backgroundRect = GUILayoutUtility.GetRect(0, 0);
  163. float xMin = backgroundRect.xMin;
  164. var labelRect = backgroundRect;
  165. labelRect.yMax += height;
  166. labelRect.xMin += 16f;
  167. labelRect.xMax -= 20f;
  168. foldoutResult.toggleState = GUI.Toggle(labelRect, toggleState, " "); // Needs a space because the checkbox won't have a proper outline if we don't make a space here
  169. foldoutResult.foldoutState = CoreEditorUtils.DrawHeaderFoldout("", foldoutState);
  170. labelRect.xMin += 20;
  171. EditorGUI.LabelField(labelRect, title, EditorStyles.boldLabel);
  172. return foldoutResult;
  173. }
  174. void OnEnable()
  175. {
  176. m_Analytics = Analytics.Renderer2DAnalytics.instance;
  177. m_ModifiedLights = new HashSet<Light2D>();
  178. m_SortingLayerDropDown = new SortingLayerDropDown();
  179. m_BlendingSettingsFoldout = new SavedBool($"{target.GetType()}.2DURPBlendingSettingsFoldout", false);
  180. m_ShadowsSettingsFoldout = new SavedBool($"{target.GetType()}.2DURPShadowsSettingsFoldout", false);
  181. m_VolumetricSettingsFoldout = new SavedBool($"{target.GetType()}.2DURPVolumetricSettingsFoldout", false);
  182. m_NormalMapsSettingsFoldout = new SavedBool($"{target.GetType()}.2DURPNormalMapsSettingsFoldout", false);
  183. m_LightType = serializedObject.FindProperty("m_LightType");
  184. m_LightColor = serializedObject.FindProperty("m_Color");
  185. m_LightIntensity = serializedObject.FindProperty("m_Intensity");
  186. m_UseNormalMap = serializedObject.FindProperty("m_UseNormalMap");
  187. m_ShadowsEnabled = serializedObject.FindProperty("m_ShadowsEnabled");
  188. m_ShadowIntensity = serializedObject.FindProperty("m_ShadowIntensity");
  189. m_ShadowSoftness = serializedObject.FindProperty("m_ShadowSoftness");
  190. m_ShadowSoftnessFalloffIntensity = serializedObject.FindProperty("m_ShadowSoftnessFalloffIntensity");
  191. m_ShadowVolumeIntensity = serializedObject.FindProperty("m_ShadowVolumeIntensity");
  192. m_ShadowVolumeIntensityEnabled = serializedObject.FindProperty("m_ShadowVolumeIntensityEnabled");
  193. m_ApplyToSortingLayers = serializedObject.FindProperty("m_ApplyToSortingLayers");
  194. m_VolumetricIntensity = serializedObject.FindProperty("m_LightVolumeIntensity");
  195. m_VolumetricEnabled = serializedObject.FindProperty("m_LightVolumeEnabled");
  196. m_BlendStyleIndex = serializedObject.FindProperty("m_BlendStyleIndex");
  197. m_FalloffIntensity = serializedObject.FindProperty("m_FalloffIntensity");
  198. m_NormalMapZDistance = serializedObject.FindProperty("m_NormalMapDistance");
  199. m_NormalMapQuality = serializedObject.FindProperty("m_NormalMapQuality");
  200. m_LightOrder = serializedObject.FindProperty("m_LightOrder");
  201. m_OverlapOperation = serializedObject.FindProperty("m_OverlapOperation");
  202. // Point Light
  203. m_PointInnerAngle = serializedObject.FindProperty("m_PointLightInnerAngle");
  204. m_PointOuterAngle = serializedObject.FindProperty("m_PointLightOuterAngle");
  205. m_PointInnerRadius = serializedObject.FindProperty("m_PointLightInnerRadius");
  206. m_PointOuterRadius = serializedObject.FindProperty("m_PointLightOuterRadius");
  207. m_DeprecatedPointLightSprite = serializedObject.FindProperty("m_DeprecatedPointLightCookieSprite");
  208. // Shape Light
  209. m_ShapeLightParametricRadius = serializedObject.FindProperty("m_ShapeLightParametricRadius");
  210. m_ShapeLightFalloffSize = serializedObject.FindProperty("m_ShapeLightFalloffSize");
  211. m_ShapeLightParametricSides = serializedObject.FindProperty("m_ShapeLightParametricSides");
  212. m_ShapeLightSprite = serializedObject.FindProperty("m_LightCookieSprite");
  213. m_AnyBlendStyleEnabled = false;
  214. var blendStyleIndices = new List<int>();
  215. var blendStyleNames = new List<string>();
  216. var rendererData = Light2DEditorUtility.GetRenderer2DData();
  217. if (rendererData != null)
  218. {
  219. for (int i = 0; i < rendererData.lightBlendStyles.Length; ++i)
  220. {
  221. blendStyleIndices.Add(i);
  222. ref var blendStyle = ref rendererData.lightBlendStyles[i];
  223. if (blendStyle.maskTextureChannel == Light2DBlendStyle.TextureChannel.None)
  224. blendStyleNames.Add(blendStyle.name);
  225. else
  226. {
  227. var name = string.Format("{0} ({1})", blendStyle.name, blendStyle.maskTextureChannel);
  228. blendStyleNames.Add(name);
  229. }
  230. m_AnyBlendStyleEnabled = true;
  231. }
  232. }
  233. else
  234. {
  235. for (int i = 0; i < 4; ++i)
  236. {
  237. blendStyleIndices.Add(i);
  238. blendStyleNames.Add("Operation" + i);
  239. }
  240. }
  241. m_BlendStyleIndices = blendStyleIndices.ToArray();
  242. m_BlendStyleNames = blendStyleNames.Select(x => new GUIContent(x)).ToArray();
  243. m_SortingLayerDropDown.OnEnable(serializedObject, "m_ApplyToSortingLayers");
  244. }
  245. internal void SendModifiedAnalytics(Analytics.Renderer2DAnalytics analytics, Light2D light)
  246. {
  247. Analytics.LightDataAnalytic lightData = new Analytics.LightDataAnalytic(light.GetInstanceID(), false, light.lightType);
  248. Analytics.Renderer2DAnalytics.instance.SendData(lightData);
  249. }
  250. void OnDestroy()
  251. {
  252. if (m_ModifiedLights != null && m_ModifiedLights.Count > 0)
  253. {
  254. foreach (Light2D light in m_ModifiedLights)
  255. {
  256. SendModifiedAnalytics(m_Analytics, light);
  257. }
  258. }
  259. }
  260. void DrawBlendingGroup()
  261. {
  262. CoreEditorUtils.DrawSplitter(false);
  263. m_BlendingSettingsFoldout.value = CoreEditorUtils.DrawHeaderFoldout(Styles.blendingSettingsFoldout, m_BlendingSettingsFoldout.value);
  264. if (m_BlendingSettingsFoldout.value)
  265. {
  266. if (!m_AnyBlendStyleEnabled)
  267. EditorGUILayout.HelpBox(Styles.generalLightNoLightEnabled);
  268. else
  269. EditorGUILayout.IntPopup(m_BlendStyleIndex, m_BlendStyleNames, m_BlendStyleIndices, Styles.generalBlendStyle);
  270. EditorGUILayout.PropertyField(m_LightOrder, Styles.generalLightOrder);
  271. EditorGUILayout.PropertyField(m_OverlapOperation, Styles.generalLightOverlapOperation);
  272. }
  273. }
  274. void DrawShadowsGroup()
  275. {
  276. CoreEditorUtils.DrawSplitter(false);
  277. ToggleFoldoutResult result = DrawHeaderFoldoutWithToggle(Styles.shadowsSettingsFoldout, m_ShadowsSettingsFoldout.value, m_ShadowsEnabled.boolValue);
  278. m_ShadowsEnabled.boolValue = result.toggleState;
  279. m_ShadowsSettingsFoldout.value = result.foldoutState;
  280. if (m_ShadowsSettingsFoldout.value)
  281. {
  282. EditorGUI.indentLevel++;
  283. EditorGUI.BeginDisabledGroup(!m_ShadowsEnabled.boolValue);
  284. EditorGUILayout.PropertyField(m_ShadowIntensity, Styles.generalShadowIntensity);
  285. EditorGUILayout.PropertyField(m_ShadowSoftness, Styles.generalShadowSoftness);
  286. EditorGUILayout.PropertyField(m_ShadowSoftnessFalloffIntensity,Styles.generalShadowSoftnessFalloffIntensity);
  287. EditorGUI.EndDisabledGroup();
  288. EditorGUI.indentLevel--;
  289. }
  290. }
  291. void DrawVolumetricGroup()
  292. {
  293. CoreEditorUtils.DrawSplitter(false);
  294. ToggleFoldoutResult result = DrawHeaderFoldoutWithToggle(Styles.volumetricSettingsFoldout, m_VolumetricSettingsFoldout.value, m_VolumetricEnabled.boolValue);
  295. m_VolumetricSettingsFoldout.value = result.foldoutState;
  296. m_VolumetricEnabled.boolValue = result.toggleState;
  297. if (m_VolumetricSettingsFoldout.value)
  298. {
  299. EditorGUI.indentLevel++;
  300. EditorGUI.BeginDisabledGroup(!m_VolumetricEnabled.boolValue);
  301. EditorGUILayout.PropertyField(m_VolumetricIntensity, Styles.generalVolumeIntensity);
  302. if (m_VolumetricIntensity.floatValue < 0)
  303. m_VolumetricIntensity.floatValue = 0;
  304. DrawToggleProperty(Styles.generalShadowVolumeIntensity, m_ShadowVolumeIntensityEnabled, m_ShadowVolumeIntensity);
  305. EditorGUI.EndDisabledGroup();
  306. EditorGUI.indentLevel--;
  307. }
  308. }
  309. void DrawNormalMapGroup()
  310. {
  311. CoreEditorUtils.DrawSplitter(false);
  312. m_NormalMapsSettingsFoldout.value = CoreEditorUtils.DrawHeaderFoldout(Styles.normalMapsSettingsFoldout, m_NormalMapsSettingsFoldout.value);
  313. if (m_NormalMapsSettingsFoldout.value)
  314. {
  315. EditorGUILayout.PropertyField(m_NormalMapQuality, Styles.generalNormalMapLightQuality);
  316. EditorGUI.BeginDisabledGroup(m_NormalMapQuality.intValue == (int)Light2D.NormalMapQuality.Disabled);
  317. EditorGUI.BeginChangeCheck();
  318. EditorGUILayout.PropertyField(m_NormalMapZDistance, Styles.generalNormalMapZDistance);
  319. if (EditorGUI.EndChangeCheck())
  320. m_NormalMapZDistance.floatValue = Mathf.Max(0.0f, m_NormalMapZDistance.floatValue);
  321. EditorGUI.EndDisabledGroup();
  322. }
  323. }
  324. void DrawFoldouts()
  325. {
  326. DrawBlendingGroup();
  327. DrawShadowsGroup();
  328. DrawVolumetricGroup();
  329. DrawNormalMapGroup();
  330. }
  331. void DrawRadiusProperties(GUIContent label, SerializedProperty innerRadius, GUIContent content1, SerializedProperty outerRadius, GUIContent content2)
  332. {
  333. GUIStyle style = GUI.skin.box;
  334. float savedLabelWidth = EditorGUIUtility.labelWidth;
  335. int savedIndentLevel = EditorGUI.indentLevel;
  336. EditorGUILayout.BeginHorizontal();
  337. EditorGUILayout.PrefixLabel(label);
  338. EditorGUILayout.BeginHorizontal();
  339. EditorGUI.indentLevel = 0;
  340. EditorGUIUtility.labelWidth = style.CalcSize(content1).x;
  341. EditorGUI.BeginChangeCheck();
  342. EditorGUILayout.PropertyField(innerRadius, content1);
  343. if (EditorGUI.EndChangeCheck())
  344. {
  345. if (innerRadius.floatValue > outerRadius.floatValue)
  346. innerRadius.floatValue = outerRadius.floatValue;
  347. else if (innerRadius.floatValue < 0)
  348. innerRadius.floatValue = 0;
  349. }
  350. EditorGUIUtility.labelWidth = style.CalcSize(content2).x;
  351. EditorGUI.BeginChangeCheck();
  352. EditorGUILayout.PropertyField(outerRadius, content2);
  353. if (EditorGUI.EndChangeCheck() && outerRadius.floatValue < innerRadius.floatValue)
  354. outerRadius.floatValue = innerRadius.floatValue;
  355. EditorGUILayout.EndHorizontal();
  356. EditorGUILayout.EndHorizontal();
  357. EditorGUIUtility.labelWidth = savedLabelWidth;
  358. EditorGUI.indentLevel = savedIndentLevel;
  359. }
  360. void DrawToggleProperty(GUIContent label, SerializedProperty boolProperty, SerializedProperty property)
  361. {
  362. int savedIndentLevel = EditorGUI.indentLevel;
  363. float savedLabelWidth = EditorGUIUtility.labelWidth;
  364. const int kCheckboxWidth = 20;
  365. EditorGUILayout.BeginHorizontal();
  366. EditorGUILayout.PropertyField(boolProperty, GUIContent.none, GUILayout.MaxWidth(kCheckboxWidth));
  367. EditorGUIUtility.labelWidth = EditorGUIUtility.labelWidth - kCheckboxWidth;
  368. EditorGUI.BeginDisabledGroup(!boolProperty.boolValue);
  369. EditorGUILayout.PropertyField(property, label);
  370. EditorGUI.EndDisabledGroup();
  371. EditorGUILayout.EndHorizontal();
  372. EditorGUI.indentLevel = savedIndentLevel;
  373. EditorGUIUtility.labelWidth = savedLabelWidth;
  374. }
  375. public void DrawInnerAndOuterSpotAngle(SerializedProperty minProperty, SerializedProperty maxProperty, GUIContent label)
  376. {
  377. float textFieldWidth = 45f;
  378. float min = minProperty.floatValue;
  379. float max = maxProperty.floatValue;
  380. var rect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight);
  381. // This widget is a little bit of a special case.
  382. // The right hand side of the min max slider will control the reset of the max value
  383. // The left hand side of the min max slider will control the reset of the min value
  384. // The label itself will not have a right click and reset value.
  385. rect = EditorGUI.PrefixLabel(rect, label);
  386. EditorGUI.BeginProperty(new Rect(rect) { width = rect.width * 0.5f }, label, minProperty);
  387. EditorGUI.BeginProperty(new Rect(rect) { xMin = rect.x + rect.width * 0.5f }, GUIContent.none, maxProperty);
  388. var minRect = new Rect(rect) { width = textFieldWidth };
  389. var maxRect = new Rect(rect) { xMin = rect.xMax - textFieldWidth };
  390. var sliderRect = new Rect(rect) { xMin = minRect.xMax + 4, xMax = maxRect.xMin - 4 };
  391. EditorGUI.BeginChangeCheck();
  392. EditorGUI.DelayedFloatField(minRect, minProperty, GUIContent.none);
  393. if (EditorGUI.EndChangeCheck())
  394. {
  395. if (minProperty.floatValue > maxProperty.floatValue)
  396. minProperty.floatValue = maxProperty.floatValue;
  397. else if (minProperty.floatValue < 0)
  398. minProperty.floatValue = 0;
  399. }
  400. EditorGUI.BeginChangeCheck();
  401. EditorGUI.MinMaxSlider(sliderRect, ref min, ref max, 0f, 360f);
  402. if (EditorGUI.EndChangeCheck())
  403. {
  404. minProperty.floatValue = min;
  405. maxProperty.floatValue = max;
  406. }
  407. EditorGUI.BeginChangeCheck();
  408. EditorGUI.DelayedFloatField(maxRect, m_PointOuterAngle, GUIContent.none);
  409. if (EditorGUI.EndChangeCheck())
  410. {
  411. if (minProperty.floatValue > maxProperty.floatValue)
  412. maxProperty.floatValue = minProperty.floatValue;
  413. else if (maxProperty.floatValue > 360)
  414. maxProperty.floatValue = 360;
  415. }
  416. EditorGUI.EndProperty();
  417. EditorGUI.EndProperty();
  418. }
  419. void DrawGlobalLight(SerializedObject serializedObject)
  420. {
  421. m_SortingLayerDropDown.OnTargetSortingLayers(serializedObject, targets, Styles.generalSortingLayerPrefixLabel, AnalyticsTrackChanges);
  422. DrawBlendingGroup();
  423. }
  424. void DrawParametricDeprecated(SerializedObject serializedObject)
  425. {
  426. GUIContent buttonText = targets.Length > 1 ? Styles.deprecatedParametricLightButtonMulti : Styles.deprecatedParametricLightButtonSingle;
  427. GUIContent helpText = targets.Length > 1 ? Styles.deprecatedParametricLightWarningMulti : Styles.deprecatedParametricLightWarningSingle;
  428. string dialogText = targets.Length > 1 ? Styles.deprecatedParametricLightDialogTextMulti : Styles.deprecatedParametricLightDialogTextSingle;
  429. EditorGUILayout.HelpBox(helpText);
  430. EditorGUILayout.Space();
  431. if (GUILayout.Button(buttonText))
  432. {
  433. if (EditorUtility.DisplayDialog(Styles.deprecatedParametricLightDialogTitle, dialogText, Styles.deprecatedParametricLightDialogProceed, Styles.deprecatedParametricLightDialogCancel))
  434. {
  435. for (int i = 0; i < targets.Length; i++)
  436. {
  437. Light2D light = (Light2D)targets[i];
  438. if (light.lightType == (Light2D.LightType)Light2D.DeprecatedLightType.Parametric)
  439. ParametricToFreeformLightUpgrader.UpgradeParametricLight(light);
  440. }
  441. }
  442. }
  443. EditorGUILayout.Space();
  444. EditorGUILayout.HelpBox(Styles.deprecatedParametricLightInstructions);
  445. }
  446. bool DrawLightCommon()
  447. {
  448. var meshChanged = false;
  449. Rect lightTypeRect = EditorGUILayout.GetControlRect();
  450. EditorGUI.BeginProperty(lightTypeRect, GUIContent.none, m_LightType);
  451. EditorGUI.BeginChangeCheck();
  452. int newLightType = EditorGUI.Popup(lightTypeRect, Styles.generalLightType, m_LightType.intValue - 1, Styles.lightTypeOptions); // -1 is a bit hacky its to support compatibiltiy. We need something better.
  453. if (EditorGUI.EndChangeCheck())
  454. {
  455. m_LightType.intValue = newLightType + 1; // -1 is a bit hacky its to support compatibiltiy. We need something better.
  456. meshChanged = true;
  457. }
  458. EditorGUI.EndProperty();
  459. // Color and intensity
  460. EditorGUILayout.PropertyField(m_LightColor, Styles.generalLightColor);
  461. EditorGUI.BeginChangeCheck();
  462. EditorGUILayout.PropertyField(m_LightIntensity, Styles.generalLightIntensity);
  463. if (EditorGUI.EndChangeCheck())
  464. m_LightIntensity.floatValue = Mathf.Max(m_LightIntensity.floatValue, 0);
  465. return meshChanged;
  466. }
  467. void DrawSpotLight(SerializedObject serializedObject)
  468. {
  469. DrawRadiusProperties(Styles.pointLightRadius, m_PointInnerRadius, Styles.pointLightInner, m_PointOuterRadius, Styles.pointLightOuter);
  470. DrawInnerAndOuterSpotAngle(m_PointInnerAngle, m_PointOuterAngle, Styles.InnerOuterSpotAngle);
  471. EditorGUILayout.Slider(m_FalloffIntensity, 0, 1, Styles.generalFalloffIntensity);
  472. if (m_DeprecatedPointLightSprite.objectReferenceValue != null)
  473. EditorGUILayout.PropertyField(m_DeprecatedPointLightSprite, Styles.pointLightSprite);
  474. m_SortingLayerDropDown.OnTargetSortingLayers(serializedObject, targets, Styles.generalSortingLayerPrefixLabel, AnalyticsTrackChanges);
  475. DrawFoldouts();
  476. }
  477. void DrawSpriteLight(SerializedObject serializedObject)
  478. {
  479. EditorGUILayout.PropertyField(m_ShapeLightSprite, Styles.shapeLightSprite);
  480. m_SortingLayerDropDown.OnTargetSortingLayers(serializedObject, targets, Styles.generalSortingLayerPrefixLabel, AnalyticsTrackChanges);
  481. DrawFoldouts();
  482. }
  483. void DrawShapeLight(SerializedObject serializedObject)
  484. {
  485. EditorGUILayout.PropertyField(m_ShapeLightFalloffSize, Styles.generalFalloffSize);
  486. if (m_ShapeLightFalloffSize.floatValue < 0)
  487. m_ShapeLightFalloffSize.floatValue = 0;
  488. EditorGUILayout.Slider(m_FalloffIntensity, 0, 1, Styles.generalFalloffIntensity);
  489. m_SortingLayerDropDown.OnTargetSortingLayers(serializedObject, targets, Styles.generalSortingLayerPrefixLabel, AnalyticsTrackChanges);
  490. if (m_LightType.intValue == (int)Light2D.LightType.Freeform)
  491. {
  492. DoEditButton<FreeformShapeTool>(PathEditorToolContents.icon, "Edit Shape");
  493. DoPathInspector<FreeformShapeTool>();
  494. DoSnappingInspector<FreeformShapeTool>();
  495. }
  496. DrawFoldouts();
  497. }
  498. Vector3 DrawAngleSlider2D(Transform transform, Quaternion rotation, float radius, float offset, Handles.CapFunction capFunc, float capSize, bool leftAngle, bool drawLine, bool useCapOffset, ref float angle)
  499. {
  500. float oldAngle = angle;
  501. float angleBy2 = (angle / 2) * (leftAngle ? -1.0f : 1.0f);
  502. Vector3 trcwPos = Quaternion.AngleAxis(angleBy2, -transform.forward) * (transform.up);
  503. Vector3 cwPos = transform.position + trcwPos * (radius + offset);
  504. float direction = leftAngle ? 1 : -1;
  505. // Offset the handle
  506. float size = .25f * capSize;
  507. Vector3 handleOffset = useCapOffset ? rotation * new Vector3(direction * size, 0, 0) : Vector3.zero;
  508. EditorGUI.BeginChangeCheck();
  509. var id = GUIUtility.GetControlID("AngleSlider".GetHashCode(), FocusType.Passive);
  510. Vector3 cwHandle = Handles.Slider2D(id, cwPos, handleOffset, Vector3.forward, rotation * Vector3.up, rotation * Vector3.right, capSize, capFunc, Vector3.zero);
  511. if (EditorGUI.EndChangeCheck())
  512. {
  513. Vector3 toCwHandle = (transform.position - cwHandle).normalized;
  514. angle = 360 - 2 * Quaternion.Angle(Quaternion.FromToRotation(transform.up, toCwHandle), Quaternion.identity);
  515. angle = Mathf.Round(angle * 100) / 100f;
  516. float side = Vector3.Dot(direction * transform.right, toCwHandle);
  517. if (side < 0)
  518. {
  519. if (oldAngle < 180)
  520. angle = 0;
  521. else
  522. angle = 360;
  523. }
  524. }
  525. if (drawLine)
  526. Handles.DrawLine(transform.position, cwHandle);
  527. return cwHandle;
  528. }
  529. private float DrawAngleHandle(Transform transform, float radius, float offset, Handles.CapFunction capLeft, Handles.CapFunction capRight, ref float angle)
  530. {
  531. float old = angle;
  532. float handleOffset = HandleUtility.GetHandleSize(transform.position) * offset;
  533. float handleSize = HandleUtility.GetHandleSize(transform.position) * k_AngleCapSize;
  534. Quaternion rotLt = Quaternion.AngleAxis(-angle / 2, -transform.forward) * transform.rotation;
  535. DrawAngleSlider2D(transform, rotLt, radius, handleOffset, capLeft, handleSize, true, true, true, ref angle);
  536. Quaternion rotRt = Quaternion.AngleAxis(angle / 2, -transform.forward) * transform.rotation;
  537. DrawAngleSlider2D(transform, rotRt, radius, handleOffset, capRight, handleSize, false, true, true, ref angle);
  538. return angle - old;
  539. }
  540. private void DrawRadiusArc(Transform transform, float radius, float angle, int steps, Handles.CapFunction capFunc, float capSize, bool even)
  541. {
  542. Handles.DrawWireArc(transform.position, transform.forward, Quaternion.AngleAxis(180 - angle / 2, transform.forward) * -transform.up, angle, radius);
  543. }
  544. Handles.CapFunction GetCapFunc(Texture texture, bool isAngleHandle)
  545. {
  546. return (controlID, position, rotation, size, eventType) => Light2DEditorUtility.GUITextureCap(controlID, texture, position, rotation, size, eventType, isAngleHandle);
  547. }
  548. private void DrawAngleHandles(Light2D light)
  549. {
  550. var oldColor = Handles.color;
  551. Handles.color = Color.yellow;
  552. float outerAngle = light.pointLightOuterAngle;
  553. float diff = DrawAngleHandle(light.transform, light.pointLightOuterRadius, k_AngleCapOffset, GetCapFunc(Styles.lightCapTopRight, true), GetCapFunc(Styles.lightCapBottomRight, true), ref outerAngle);
  554. light.pointLightOuterAngle = outerAngle;
  555. if (diff != 0.0f)
  556. light.pointLightInnerAngle = Mathf.Max(0.0f, light.pointLightInnerAngle + diff);
  557. float innerAngle = light.pointLightInnerAngle;
  558. diff = DrawAngleHandle(light.transform, light.pointLightOuterRadius, -k_AngleCapOffset, GetCapFunc(Styles.lightCapTopLeft, true), GetCapFunc(Styles.lightCapBottomLeft, true), ref innerAngle);
  559. light.pointLightInnerAngle = innerAngle;
  560. if (diff != 0.0f)
  561. light.pointLightInnerAngle = light.pointLightInnerAngle < light.pointLightOuterAngle ? light.pointLightInnerAngle : light.pointLightOuterAngle;
  562. light.pointLightInnerAngle = Mathf.Min(light.pointLightInnerAngle, light.pointLightOuterAngle);
  563. Handles.color = oldColor;
  564. }
  565. private void DrawRangeHandles(Light2D light)
  566. {
  567. var dummy = 0.0f;
  568. bool radiusChanged = false;
  569. Vector3 handlePos = Vector3.zero;
  570. Quaternion rotLeft = Quaternion.AngleAxis(0, -light.transform.forward) * light.transform.rotation;
  571. float handleOffset = HandleUtility.GetHandleSize(light.transform.position) * k_AngleCapOffsetSecondary;
  572. float handleSize = HandleUtility.GetHandleSize(light.transform.position) * k_AngleCapSize;
  573. var oldColor = Handles.color;
  574. Handles.color = Color.yellow;
  575. float outerRadius = light.pointLightOuterRadius;
  576. EditorGUI.BeginChangeCheck();
  577. Vector3 returnPos = DrawAngleSlider2D(light.transform, rotLeft, outerRadius, -handleOffset, GetCapFunc(Styles.lightCapUp, false), handleSize, false, false, false, ref dummy);
  578. if (EditorGUI.EndChangeCheck())
  579. {
  580. var vec = (returnPos - light.transform.position).normalized;
  581. light.transform.up = new Vector3(vec.x, vec.y, 0);
  582. outerRadius = (returnPos - light.transform.position).magnitude;
  583. outerRadius = outerRadius + handleOffset;
  584. radiusChanged = true;
  585. }
  586. DrawRadiusArc(light.transform, light.pointLightOuterRadius, light.pointLightOuterAngle, 0, Handles.DotHandleCap, k_RangeCapSize, false);
  587. Handles.color = Color.gray;
  588. float innerRadius = light.pointLightInnerRadius;
  589. EditorGUI.BeginChangeCheck();
  590. returnPos = DrawAngleSlider2D(light.transform, rotLeft, innerRadius, handleOffset, GetCapFunc(Styles.lightCapDown, false), handleSize, true, false, false, ref dummy);
  591. if (EditorGUI.EndChangeCheck())
  592. {
  593. innerRadius = (returnPos - light.transform.position).magnitude;
  594. innerRadius = innerRadius - handleOffset;
  595. radiusChanged = true;
  596. }
  597. DrawRadiusArc(light.transform, light.pointLightInnerRadius, light.pointLightOuterAngle, 0, Handles.SphereHandleCap, k_InnerRangeCapSize, false);
  598. Handles.color = oldColor;
  599. if (radiusChanged)
  600. {
  601. light.pointLightInnerRadius = (outerRadius < innerRadius) ? outerRadius : innerRadius;
  602. light.pointLightOuterRadius = (innerRadius > outerRadius) ? innerRadius : outerRadius;
  603. }
  604. }
  605. void OnSceneGUI()
  606. {
  607. var light = target as Light2D;
  608. if (light == null)
  609. return;
  610. Transform t = light.transform;
  611. switch (light.lightType)
  612. {
  613. case Light2D.LightType.Point:
  614. {
  615. Undo.RecordObject(light.transform, "Edit Point Light Transform");
  616. Undo.RecordObject(light, "Edit Point Light");
  617. DrawRangeHandles(light);
  618. DrawAngleHandles(light);
  619. if (GUI.changed)
  620. EditorUtility.SetDirty(light);
  621. }
  622. break;
  623. case Light2D.LightType.Sprite:
  624. {
  625. var cookieSprite = light.lightCookieSprite;
  626. if (cookieSprite != null)
  627. {
  628. Vector3 min = cookieSprite.bounds.min;
  629. Vector3 max = cookieSprite.bounds.max;
  630. Vector3 v0 = t.TransformPoint(new Vector3(min.x, min.y));
  631. Vector3 v1 = t.TransformPoint(new Vector3(max.x, min.y));
  632. Vector3 v2 = t.TransformPoint(new Vector3(max.x, max.y));
  633. Vector3 v3 = t.TransformPoint(new Vector3(min.x, max.y));
  634. Handles.DrawLine(v0, v1);
  635. Handles.DrawLine(v1, v2);
  636. Handles.DrawLine(v2, v3);
  637. Handles.DrawLine(v3, v0);
  638. }
  639. }
  640. break;
  641. case Light2D.LightType.Freeform:
  642. {
  643. // Draw the falloff shape's outline
  644. List<Vector2> falloffShape = light.GetFalloffShape();
  645. Handles.color = Color.white;
  646. for (int i = 0; i < falloffShape.Count - 1; ++i)
  647. {
  648. Handles.DrawLine(t.TransformPoint(falloffShape[i]), t.TransformPoint(falloffShape[i + 1]));
  649. }
  650. if (falloffShape.Count > 0)
  651. Handles.DrawLine(t.TransformPoint(falloffShape[falloffShape.Count - 1]), t.TransformPoint(falloffShape[0]));
  652. for (int i = 0; i < light.shapePath.Length - 1; ++i)
  653. {
  654. Handles.DrawLine(t.TransformPoint(light.shapePath[i]),
  655. t.TransformPoint(light.shapePath[i + 1]));
  656. }
  657. if (light.shapePath.Length > 0)
  658. Handles.DrawLine(t.TransformPoint(light.shapePath[light.shapePath.Length - 1]), t.TransformPoint(light.shapePath[0]));
  659. }
  660. break;
  661. }
  662. }
  663. public override void OnInspectorGUI()
  664. {
  665. var meshChanged = false;
  666. serializedObject.Update();
  667. UniversalRenderPipelineAsset asset = UniversalRenderPipeline.asset;
  668. if (asset != null)
  669. {
  670. if (!Light2DEditorUtility.IsUsing2DRenderer())
  671. {
  672. EditorGUILayout.HelpBox(Styles.asset2DUnassignedWarning);
  673. }
  674. else
  675. {
  676. if (m_LightType.intValue != (int)Light2D.DeprecatedLightType.Parametric)
  677. meshChanged = DrawLightCommon();
  678. switch (m_LightType.intValue)
  679. {
  680. case (int)Light2D.LightType.Point:
  681. {
  682. DrawSpotLight(serializedObject);
  683. }
  684. break;
  685. case (int)Light2D.LightType.Freeform:
  686. {
  687. DrawShapeLight(serializedObject);
  688. }
  689. break;
  690. case (int)Light2D.LightType.Sprite:
  691. {
  692. DrawSpriteLight(serializedObject);
  693. }
  694. break;
  695. case (int)Light2D.LightType.Global:
  696. {
  697. DrawGlobalLight(serializedObject);
  698. }
  699. break;
  700. case (int)Light2D.DeprecatedLightType.Parametric:
  701. {
  702. DrawParametricDeprecated(serializedObject);
  703. }
  704. break;
  705. }
  706. AnalyticsTrackChanges(serializedObject);
  707. if (serializedObject.ApplyModifiedProperties())
  708. {
  709. if (meshChanged)
  710. lightObject.UpdateMesh();
  711. }
  712. }
  713. }
  714. else
  715. {
  716. EditorGUILayout.HelpBox(Styles.renderPipelineUnassignedWarning);
  717. if (meshChanged)
  718. lightObject.UpdateMesh();
  719. }
  720. }
  721. }
  722. internal class Light2DPostProcess : AssetPostprocessor
  723. {
  724. void OnPostprocessSprites(Texture2D texture, Sprite[] sprites)
  725. {
  726. var lights = Resources.FindObjectsOfTypeAll<Light2D>().Where(x => x.lightType == Light2D.LightType.Sprite && x.lightCookieSprite == null);
  727. foreach (var light in lights)
  728. light.MarkForUpdate();
  729. }
  730. }
  731. }