UnityGame/Library/PackageCache/com.unity.render-pipelines.universal/Editor/2D/Light2DEditor.cs
2024-10-27 10:53:47 +03:00

890 lines
45 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections.Generic;
using System.Linq;
using UnityEditor.EditorTools;
using UnityEditor.Rendering.Universal.Path2D;
using UnityEngine;
using UnityEngine.Rendering.Universal;
namespace UnityEditor.Rendering.Universal
{
[CustomEditor(typeof(Light2D))]
[CanEditMultipleObjects]
internal class Light2DEditor : PathComponentEditor<ScriptablePath>
{
[EditorTool("Edit Freeform Shape", typeof(Light2D))]
class FreeformShapeTool : PathEditorTool<ScriptablePath>
{
const string k_ShapePath = "m_ShapePath";
public override bool IsAvailable()
{
var light = target as Light2D;
if (light == null)
return false;
else
return base.IsAvailable() && light.lightType == Light2D.LightType.Freeform;
}
protected override IShape GetShape(Object target)
{
return (target as Light2D).shapePath.ToPolygon(false);
}
protected override void SetShape(ScriptablePath shapeEditor, SerializedObject serializedObject)
{
serializedObject.Update();
var pointsProperty = serializedObject.FindProperty(k_ShapePath);
pointsProperty.arraySize = shapeEditor.pointCount;
for (var i = 0; i < shapeEditor.pointCount; ++i)
pointsProperty.GetArrayElementAtIndex(i).vector3Value = shapeEditor.GetPoint(i).position;
((Light2D)(serializedObject.targetObject)).UpdateMesh();
// This is untracked right now...
serializedObject.ApplyModifiedProperties();
}
}
private struct ToggleFoldoutResult
{
public bool foldoutState;
public bool toggleState;
}
private static class Styles
{
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 Lights beam.");
public static Texture lightCapTopRight = Resources.Load<Texture>("LightCapTopRight");
public static Texture lightCapTopLeft = Resources.Load<Texture>("LightCapTopLeft");
public static Texture lightCapBottomLeft = Resources.Load<Texture>("LightCapBottomLeft");
public static Texture lightCapBottomRight = Resources.Load<Texture>("LightCapBottomRight");
public static Texture lightCapUp = Resources.Load<Texture>("LightCapUp");
public static Texture lightCapDown = Resources.Load<Texture>("LightCapDown");
public static GUIContent lightTypeFreeform = new GUIContent("Freeform", Resources.Load("InspectorIcons/FreeformLight") as Texture);
public static GUIContent lightTypeSprite = new GUIContent("Sprite", Resources.Load("InspectorIcons/SpriteLight") as Texture);
public static GUIContent lightTypePoint = new GUIContent("Spot", Resources.Load("InspectorIcons/PointLight") as Texture);
public static GUIContent lightTypeGlobal = new GUIContent("Global", Resources.Load("InspectorIcons/GlobalLight") as Texture);
public static GUIContent[] lightTypeOptions = new GUIContent[] { lightTypeFreeform, lightTypeSprite, lightTypePoint, lightTypeGlobal };
public static GUIContent blendingSettingsFoldout = EditorGUIUtility.TrTextContent("Blending", "Options used for blending");
public static GUIContent shadowsSettingsFoldout = EditorGUIUtility.TrTextContent("Shadows", "Options used for shadows");
public static GUIContent volumetricSettingsFoldout = EditorGUIUtility.TrTextContent("Volumetric", "Options used for volumetric lighting");
public static GUIContent normalMapsSettingsFoldout = EditorGUIUtility.TrTextContent("Normal Maps", "Options used for normal maps");
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.");
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.");
public static GUIContent generalFalloffIntensity = EditorGUIUtility.TrTextContent("Falloff Strength", "Adjusts the falloff curve to control the softness of this lights edges. The higher the falloff strength, the softer the edges of this light.");
public static GUIContent generalLightColor = EditorGUIUtility.TrTextContent("Color", "Adjusts this lights color.");
public static GUIContent generalLightIntensity = EditorGUIUtility.TrTextContent("Intensity", "Adjusts this lights color intensity by using multiply to brighten the Sprite beyond its original color.");
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.");
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.");
public static GUIContent generalLightOverlapOperation = EditorGUIUtility.TrTextContent("Overlap Operation", "Determines how this light blends with the other lights either through additive or alpha blending.");
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.");
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.");
public static GUIContent generalShadowSoftness = EditorGUIUtility.TrTextContent("Softness", "Adjusts the amount of softness at the edge of the shadow.");
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.");
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.");
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.");
public static GUIContent generalLightNoLightEnabled = EditorGUIUtility.TrTextContentWithIcon("No valid blend styles are enabled.", MessageType.Error);
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.");
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.");
public static GUIContent pointLightRadius = EditorGUIUtility.TrTextContent("Radius", "Adjusts the inner / outer radius of this light to change the size of this light.");
public static GUIContent pointLightInner = EditorGUIUtility.TrTextContent("Inner", "Specify the inner radius of the light");
public static GUIContent pointLightOuter = EditorGUIUtility.TrTextContent("Outer", "Specify the outer radius of the light");
public static GUIContent pointLightSprite = EditorGUIUtility.TrTextContent("Sprite", "Specify the sprite (deprecated)");
public static GUIContent shapeLightSprite = EditorGUIUtility.TrTextContent("Sprite", "Assign a Sprite which acts as a mask to create a light cookie.");
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);
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);
public static GUIContent deprecatedParametricLightInstructions = EditorGUIUtility.TrTextContent("Alternatively, you may choose to upgrade from the menu. Window > Rendering > Render Pipeline Converter > URP 2D Converters");
public static GUIContent deprecatedParametricLightButtonSingle = EditorGUIUtility.TrTextContent("Upgrade Parametric Light");
public static GUIContent deprecatedParametricLightButtonMulti = EditorGUIUtility.TrTextContent("Upgrade Parametric Lights");
public static GUIContent renderPipelineUnassignedWarning = EditorGUIUtility.TrTextContentWithIcon("Universal scriptable renderpipeline asset must be assigned in Graphics Settings or Quality Settings.", MessageType.Warning);
public static GUIContent asset2DUnassignedWarning = EditorGUIUtility.TrTextContentWithIcon("2D renderer data must be assigned to your universal render pipeline asset or camera.", MessageType.Warning);
public static string deprecatedParametricLightDialogTextSingle = "The upgrade will convert the selected parametric light into a freeform light. You can't undo this operation.";
public static string deprecatedParametricLightDialogTextMulti = "The upgrade will convert the selected parametric lights into freeform lights. You can't undo this operation.";
public static string deprecatedParametricLightDialogTitle = "Parametric Light Upgrader";
public static string deprecatedParametricLightDialogProceed = "Proceed";
public static string deprecatedParametricLightDialogCancel = "Cancel";
}
const float k_GlobalLightGizmoSize = 1.2f;
const float k_AngleCapSize = 0.16f * k_GlobalLightGizmoSize;
const float k_AngleCapOffset = 0.08f * k_GlobalLightGizmoSize;
const float k_AngleCapOffsetSecondary = -0.05f;
const float k_RangeCapSize = 0.025f * k_GlobalLightGizmoSize;
const float k_InnerRangeCapSize = 0.08f * k_GlobalLightGizmoSize;
SerializedProperty m_LightType;
SerializedProperty m_LightColor;
SerializedProperty m_LightIntensity;
SerializedProperty m_UseNormalMap;
SerializedProperty m_ShadowsEnabled;
SerializedProperty m_ShadowIntensity;
SerializedProperty m_ShadowSoftness;
SerializedProperty m_ShadowSoftnessFalloffIntensity;
SerializedProperty m_ShadowVolumeIntensity;
SerializedProperty m_ShadowVolumeIntensityEnabled;
SerializedProperty m_ApplyToSortingLayers;
SerializedProperty m_VolumetricIntensity;
SerializedProperty m_VolumetricEnabled;
SerializedProperty m_BlendStyleIndex;
SerializedProperty m_FalloffIntensity;
SerializedProperty m_NormalMapZDistance;
SerializedProperty m_NormalMapQuality;
SerializedProperty m_LightOrder;
SerializedProperty m_OverlapOperation;
// Point Light Properties
SerializedProperty m_PointInnerAngle;
SerializedProperty m_PointOuterAngle;
SerializedProperty m_PointInnerRadius;
SerializedProperty m_PointOuterRadius;
SerializedProperty m_DeprecatedPointLightSprite;
// Shape Light Properties
SerializedProperty m_ShapeLightParametricRadius;
SerializedProperty m_ShapeLightFalloffSize;
SerializedProperty m_ShapeLightParametricSides;
SerializedProperty m_ShapeLightSprite;
SavedBool m_BlendingSettingsFoldout;
SavedBool m_ShadowsSettingsFoldout;
SavedBool m_VolumetricSettingsFoldout;
SavedBool m_NormalMapsSettingsFoldout;
int[] m_BlendStyleIndices;
GUIContent[] m_BlendStyleNames;
bool m_AnyBlendStyleEnabled = false;
SortingLayerDropDown m_SortingLayerDropDown;
Light2D lightObject => target as Light2D;
Analytics.Renderer2DAnalytics m_Analytics;
HashSet<Light2D> m_ModifiedLights;
private void AnalyticsTrackChanges(SerializedObject serializedObject)
{
if (serializedObject.hasModifiedProperties)
{
foreach (Object targetObj in serializedObject.targetObjects)
{
Light2D light2d = (Light2D)targetObj;
if (!m_ModifiedLights.Contains(light2d))
m_ModifiedLights.Add(light2d);
}
}
}
private ToggleFoldoutResult DrawHeaderFoldoutWithToggle(GUIContent title, bool foldoutState, bool toggleState, string documentationURL = "")
{
ToggleFoldoutResult foldoutResult = new ToggleFoldoutResult();
const float height = 17f;
var backgroundRect = GUILayoutUtility.GetRect(0, 0);
float xMin = backgroundRect.xMin;
var labelRect = backgroundRect;
labelRect.yMax += height;
labelRect.xMin += 16f;
labelRect.xMax -= 20f;
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
foldoutResult.foldoutState = CoreEditorUtils.DrawHeaderFoldout("", foldoutState);
labelRect.xMin += 20;
EditorGUI.LabelField(labelRect, title, EditorStyles.boldLabel);
return foldoutResult;
}
void OnEnable()
{
m_Analytics = Analytics.Renderer2DAnalytics.instance;
m_ModifiedLights = new HashSet<Light2D>();
m_SortingLayerDropDown = new SortingLayerDropDown();
m_BlendingSettingsFoldout = new SavedBool($"{target.GetType()}.2DURPBlendingSettingsFoldout", false);
m_ShadowsSettingsFoldout = new SavedBool($"{target.GetType()}.2DURPShadowsSettingsFoldout", false);
m_VolumetricSettingsFoldout = new SavedBool($"{target.GetType()}.2DURPVolumetricSettingsFoldout", false);
m_NormalMapsSettingsFoldout = new SavedBool($"{target.GetType()}.2DURPNormalMapsSettingsFoldout", false);
m_LightType = serializedObject.FindProperty("m_LightType");
m_LightColor = serializedObject.FindProperty("m_Color");
m_LightIntensity = serializedObject.FindProperty("m_Intensity");
m_UseNormalMap = serializedObject.FindProperty("m_UseNormalMap");
m_ShadowsEnabled = serializedObject.FindProperty("m_ShadowsEnabled");
m_ShadowIntensity = serializedObject.FindProperty("m_ShadowIntensity");
m_ShadowSoftness = serializedObject.FindProperty("m_ShadowSoftness");
m_ShadowSoftnessFalloffIntensity = serializedObject.FindProperty("m_ShadowSoftnessFalloffIntensity");
m_ShadowVolumeIntensity = serializedObject.FindProperty("m_ShadowVolumeIntensity");
m_ShadowVolumeIntensityEnabled = serializedObject.FindProperty("m_ShadowVolumeIntensityEnabled");
m_ApplyToSortingLayers = serializedObject.FindProperty("m_ApplyToSortingLayers");
m_VolumetricIntensity = serializedObject.FindProperty("m_LightVolumeIntensity");
m_VolumetricEnabled = serializedObject.FindProperty("m_LightVolumeEnabled");
m_BlendStyleIndex = serializedObject.FindProperty("m_BlendStyleIndex");
m_FalloffIntensity = serializedObject.FindProperty("m_FalloffIntensity");
m_NormalMapZDistance = serializedObject.FindProperty("m_NormalMapDistance");
m_NormalMapQuality = serializedObject.FindProperty("m_NormalMapQuality");
m_LightOrder = serializedObject.FindProperty("m_LightOrder");
m_OverlapOperation = serializedObject.FindProperty("m_OverlapOperation");
// Point Light
m_PointInnerAngle = serializedObject.FindProperty("m_PointLightInnerAngle");
m_PointOuterAngle = serializedObject.FindProperty("m_PointLightOuterAngle");
m_PointInnerRadius = serializedObject.FindProperty("m_PointLightInnerRadius");
m_PointOuterRadius = serializedObject.FindProperty("m_PointLightOuterRadius");
m_DeprecatedPointLightSprite = serializedObject.FindProperty("m_DeprecatedPointLightCookieSprite");
// Shape Light
m_ShapeLightParametricRadius = serializedObject.FindProperty("m_ShapeLightParametricRadius");
m_ShapeLightFalloffSize = serializedObject.FindProperty("m_ShapeLightFalloffSize");
m_ShapeLightParametricSides = serializedObject.FindProperty("m_ShapeLightParametricSides");
m_ShapeLightSprite = serializedObject.FindProperty("m_LightCookieSprite");
m_AnyBlendStyleEnabled = false;
var blendStyleIndices = new List<int>();
var blendStyleNames = new List<string>();
var rendererData = Light2DEditorUtility.GetRenderer2DData();
if (rendererData != null)
{
for (int i = 0; i < rendererData.lightBlendStyles.Length; ++i)
{
blendStyleIndices.Add(i);
ref var blendStyle = ref rendererData.lightBlendStyles[i];
if (blendStyle.maskTextureChannel == Light2DBlendStyle.TextureChannel.None)
blendStyleNames.Add(blendStyle.name);
else
{
var name = string.Format("{0} ({1})", blendStyle.name, blendStyle.maskTextureChannel);
blendStyleNames.Add(name);
}
m_AnyBlendStyleEnabled = true;
}
}
else
{
for (int i = 0; i < 4; ++i)
{
blendStyleIndices.Add(i);
blendStyleNames.Add("Operation" + i);
}
}
m_BlendStyleIndices = blendStyleIndices.ToArray();
m_BlendStyleNames = blendStyleNames.Select(x => new GUIContent(x)).ToArray();
m_SortingLayerDropDown.OnEnable(serializedObject, "m_ApplyToSortingLayers");
}
internal void SendModifiedAnalytics(Analytics.Renderer2DAnalytics analytics, Light2D light)
{
Analytics.LightDataAnalytic lightData = new Analytics.LightDataAnalytic(light.GetInstanceID(), false, light.lightType);
Analytics.Renderer2DAnalytics.instance.SendData(lightData);
}
void OnDestroy()
{
if (m_ModifiedLights != null && m_ModifiedLights.Count > 0)
{
foreach (Light2D light in m_ModifiedLights)
{
SendModifiedAnalytics(m_Analytics, light);
}
}
}
void DrawBlendingGroup()
{
CoreEditorUtils.DrawSplitter(false);
m_BlendingSettingsFoldout.value = CoreEditorUtils.DrawHeaderFoldout(Styles.blendingSettingsFoldout, m_BlendingSettingsFoldout.value);
if (m_BlendingSettingsFoldout.value)
{
if (!m_AnyBlendStyleEnabled)
EditorGUILayout.HelpBox(Styles.generalLightNoLightEnabled);
else
EditorGUILayout.IntPopup(m_BlendStyleIndex, m_BlendStyleNames, m_BlendStyleIndices, Styles.generalBlendStyle);
EditorGUILayout.PropertyField(m_LightOrder, Styles.generalLightOrder);
EditorGUILayout.PropertyField(m_OverlapOperation, Styles.generalLightOverlapOperation);
}
}
void DrawShadowsGroup()
{
CoreEditorUtils.DrawSplitter(false);
ToggleFoldoutResult result = DrawHeaderFoldoutWithToggle(Styles.shadowsSettingsFoldout, m_ShadowsSettingsFoldout.value, m_ShadowsEnabled.boolValue);
m_ShadowsEnabled.boolValue = result.toggleState;
m_ShadowsSettingsFoldout.value = result.foldoutState;
if (m_ShadowsSettingsFoldout.value)
{
EditorGUI.indentLevel++;
EditorGUI.BeginDisabledGroup(!m_ShadowsEnabled.boolValue);
EditorGUILayout.PropertyField(m_ShadowIntensity, Styles.generalShadowIntensity);
EditorGUILayout.PropertyField(m_ShadowSoftness, Styles.generalShadowSoftness);
EditorGUILayout.PropertyField(m_ShadowSoftnessFalloffIntensity,Styles.generalShadowSoftnessFalloffIntensity);
EditorGUI.EndDisabledGroup();
EditorGUI.indentLevel--;
}
}
void DrawVolumetricGroup()
{
CoreEditorUtils.DrawSplitter(false);
ToggleFoldoutResult result = DrawHeaderFoldoutWithToggle(Styles.volumetricSettingsFoldout, m_VolumetricSettingsFoldout.value, m_VolumetricEnabled.boolValue);
m_VolumetricSettingsFoldout.value = result.foldoutState;
m_VolumetricEnabled.boolValue = result.toggleState;
if (m_VolumetricSettingsFoldout.value)
{
EditorGUI.indentLevel++;
EditorGUI.BeginDisabledGroup(!m_VolumetricEnabled.boolValue);
EditorGUILayout.PropertyField(m_VolumetricIntensity, Styles.generalVolumeIntensity);
if (m_VolumetricIntensity.floatValue < 0)
m_VolumetricIntensity.floatValue = 0;
DrawToggleProperty(Styles.generalShadowVolumeIntensity, m_ShadowVolumeIntensityEnabled, m_ShadowVolumeIntensity);
EditorGUI.EndDisabledGroup();
EditorGUI.indentLevel--;
}
}
void DrawNormalMapGroup()
{
CoreEditorUtils.DrawSplitter(false);
m_NormalMapsSettingsFoldout.value = CoreEditorUtils.DrawHeaderFoldout(Styles.normalMapsSettingsFoldout, m_NormalMapsSettingsFoldout.value);
if (m_NormalMapsSettingsFoldout.value)
{
EditorGUILayout.PropertyField(m_NormalMapQuality, Styles.generalNormalMapLightQuality);
EditorGUI.BeginDisabledGroup(m_NormalMapQuality.intValue == (int)Light2D.NormalMapQuality.Disabled);
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_NormalMapZDistance, Styles.generalNormalMapZDistance);
if (EditorGUI.EndChangeCheck())
m_NormalMapZDistance.floatValue = Mathf.Max(0.0f, m_NormalMapZDistance.floatValue);
EditorGUI.EndDisabledGroup();
}
}
void DrawFoldouts()
{
DrawBlendingGroup();
DrawShadowsGroup();
DrawVolumetricGroup();
DrawNormalMapGroup();
}
void DrawRadiusProperties(GUIContent label, SerializedProperty innerRadius, GUIContent content1, SerializedProperty outerRadius, GUIContent content2)
{
GUIStyle style = GUI.skin.box;
float savedLabelWidth = EditorGUIUtility.labelWidth;
int savedIndentLevel = EditorGUI.indentLevel;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel(label);
EditorGUILayout.BeginHorizontal();
EditorGUI.indentLevel = 0;
EditorGUIUtility.labelWidth = style.CalcSize(content1).x;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(innerRadius, content1);
if (EditorGUI.EndChangeCheck())
{
if (innerRadius.floatValue > outerRadius.floatValue)
innerRadius.floatValue = outerRadius.floatValue;
else if (innerRadius.floatValue < 0)
innerRadius.floatValue = 0;
}
EditorGUIUtility.labelWidth = style.CalcSize(content2).x;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(outerRadius, content2);
if (EditorGUI.EndChangeCheck() && outerRadius.floatValue < innerRadius.floatValue)
outerRadius.floatValue = innerRadius.floatValue;
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndHorizontal();
EditorGUIUtility.labelWidth = savedLabelWidth;
EditorGUI.indentLevel = savedIndentLevel;
}
void DrawToggleProperty(GUIContent label, SerializedProperty boolProperty, SerializedProperty property)
{
int savedIndentLevel = EditorGUI.indentLevel;
float savedLabelWidth = EditorGUIUtility.labelWidth;
const int kCheckboxWidth = 20;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(boolProperty, GUIContent.none, GUILayout.MaxWidth(kCheckboxWidth));
EditorGUIUtility.labelWidth = EditorGUIUtility.labelWidth - kCheckboxWidth;
EditorGUI.BeginDisabledGroup(!boolProperty.boolValue);
EditorGUILayout.PropertyField(property, label);
EditorGUI.EndDisabledGroup();
EditorGUILayout.EndHorizontal();
EditorGUI.indentLevel = savedIndentLevel;
EditorGUIUtility.labelWidth = savedLabelWidth;
}
public void DrawInnerAndOuterSpotAngle(SerializedProperty minProperty, SerializedProperty maxProperty, GUIContent label)
{
float textFieldWidth = 45f;
float min = minProperty.floatValue;
float max = maxProperty.floatValue;
var rect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight);
// This widget is a little bit of a special case.
// The right hand side of the min max slider will control the reset of the max value
// The left hand side of the min max slider will control the reset of the min value
// The label itself will not have a right click and reset value.
rect = EditorGUI.PrefixLabel(rect, label);
EditorGUI.BeginProperty(new Rect(rect) { width = rect.width * 0.5f }, label, minProperty);
EditorGUI.BeginProperty(new Rect(rect) { xMin = rect.x + rect.width * 0.5f }, GUIContent.none, maxProperty);
var minRect = new Rect(rect) { width = textFieldWidth };
var maxRect = new Rect(rect) { xMin = rect.xMax - textFieldWidth };
var sliderRect = new Rect(rect) { xMin = minRect.xMax + 4, xMax = maxRect.xMin - 4 };
EditorGUI.BeginChangeCheck();
EditorGUI.DelayedFloatField(minRect, minProperty, GUIContent.none);
if (EditorGUI.EndChangeCheck())
{
if (minProperty.floatValue > maxProperty.floatValue)
minProperty.floatValue = maxProperty.floatValue;
else if (minProperty.floatValue < 0)
minProperty.floatValue = 0;
}
EditorGUI.BeginChangeCheck();
EditorGUI.MinMaxSlider(sliderRect, ref min, ref max, 0f, 360f);
if (EditorGUI.EndChangeCheck())
{
minProperty.floatValue = min;
maxProperty.floatValue = max;
}
EditorGUI.BeginChangeCheck();
EditorGUI.DelayedFloatField(maxRect, m_PointOuterAngle, GUIContent.none);
if (EditorGUI.EndChangeCheck())
{
if (minProperty.floatValue > maxProperty.floatValue)
maxProperty.floatValue = minProperty.floatValue;
else if (maxProperty.floatValue > 360)
maxProperty.floatValue = 360;
}
EditorGUI.EndProperty();
EditorGUI.EndProperty();
}
void DrawGlobalLight(SerializedObject serializedObject)
{
m_SortingLayerDropDown.OnTargetSortingLayers(serializedObject, targets, Styles.generalSortingLayerPrefixLabel, AnalyticsTrackChanges);
DrawBlendingGroup();
}
void DrawParametricDeprecated(SerializedObject serializedObject)
{
GUIContent buttonText = targets.Length > 1 ? Styles.deprecatedParametricLightButtonMulti : Styles.deprecatedParametricLightButtonSingle;
GUIContent helpText = targets.Length > 1 ? Styles.deprecatedParametricLightWarningMulti : Styles.deprecatedParametricLightWarningSingle;
string dialogText = targets.Length > 1 ? Styles.deprecatedParametricLightDialogTextMulti : Styles.deprecatedParametricLightDialogTextSingle;
EditorGUILayout.HelpBox(helpText);
EditorGUILayout.Space();
if (GUILayout.Button(buttonText))
{
if (EditorUtility.DisplayDialog(Styles.deprecatedParametricLightDialogTitle, dialogText, Styles.deprecatedParametricLightDialogProceed, Styles.deprecatedParametricLightDialogCancel))
{
for (int i = 0; i < targets.Length; i++)
{
Light2D light = (Light2D)targets[i];
if (light.lightType == (Light2D.LightType)Light2D.DeprecatedLightType.Parametric)
ParametricToFreeformLightUpgrader.UpgradeParametricLight(light);
}
}
}
EditorGUILayout.Space();
EditorGUILayout.HelpBox(Styles.deprecatedParametricLightInstructions);
}
bool DrawLightCommon()
{
var meshChanged = false;
Rect lightTypeRect = EditorGUILayout.GetControlRect();
EditorGUI.BeginProperty(lightTypeRect, GUIContent.none, m_LightType);
EditorGUI.BeginChangeCheck();
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.
if (EditorGUI.EndChangeCheck())
{
m_LightType.intValue = newLightType + 1; // -1 is a bit hacky its to support compatibiltiy. We need something better.
meshChanged = true;
}
EditorGUI.EndProperty();
// Color and intensity
EditorGUILayout.PropertyField(m_LightColor, Styles.generalLightColor);
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_LightIntensity, Styles.generalLightIntensity);
if (EditorGUI.EndChangeCheck())
m_LightIntensity.floatValue = Mathf.Max(m_LightIntensity.floatValue, 0);
return meshChanged;
}
void DrawSpotLight(SerializedObject serializedObject)
{
DrawRadiusProperties(Styles.pointLightRadius, m_PointInnerRadius, Styles.pointLightInner, m_PointOuterRadius, Styles.pointLightOuter);
DrawInnerAndOuterSpotAngle(m_PointInnerAngle, m_PointOuterAngle, Styles.InnerOuterSpotAngle);
EditorGUILayout.Slider(m_FalloffIntensity, 0, 1, Styles.generalFalloffIntensity);
if (m_DeprecatedPointLightSprite.objectReferenceValue != null)
EditorGUILayout.PropertyField(m_DeprecatedPointLightSprite, Styles.pointLightSprite);
m_SortingLayerDropDown.OnTargetSortingLayers(serializedObject, targets, Styles.generalSortingLayerPrefixLabel, AnalyticsTrackChanges);
DrawFoldouts();
}
void DrawSpriteLight(SerializedObject serializedObject)
{
EditorGUILayout.PropertyField(m_ShapeLightSprite, Styles.shapeLightSprite);
m_SortingLayerDropDown.OnTargetSortingLayers(serializedObject, targets, Styles.generalSortingLayerPrefixLabel, AnalyticsTrackChanges);
DrawFoldouts();
}
void DrawShapeLight(SerializedObject serializedObject)
{
EditorGUILayout.PropertyField(m_ShapeLightFalloffSize, Styles.generalFalloffSize);
if (m_ShapeLightFalloffSize.floatValue < 0)
m_ShapeLightFalloffSize.floatValue = 0;
EditorGUILayout.Slider(m_FalloffIntensity, 0, 1, Styles.generalFalloffIntensity);
m_SortingLayerDropDown.OnTargetSortingLayers(serializedObject, targets, Styles.generalSortingLayerPrefixLabel, AnalyticsTrackChanges);
if (m_LightType.intValue == (int)Light2D.LightType.Freeform)
{
DoEditButton<FreeformShapeTool>(PathEditorToolContents.icon, "Edit Shape");
DoPathInspector<FreeformShapeTool>();
DoSnappingInspector<FreeformShapeTool>();
}
DrawFoldouts();
}
Vector3 DrawAngleSlider2D(Transform transform, Quaternion rotation, float radius, float offset, Handles.CapFunction capFunc, float capSize, bool leftAngle, bool drawLine, bool useCapOffset, ref float angle)
{
float oldAngle = angle;
float angleBy2 = (angle / 2) * (leftAngle ? -1.0f : 1.0f);
Vector3 trcwPos = Quaternion.AngleAxis(angleBy2, -transform.forward) * (transform.up);
Vector3 cwPos = transform.position + trcwPos * (radius + offset);
float direction = leftAngle ? 1 : -1;
// Offset the handle
float size = .25f * capSize;
Vector3 handleOffset = useCapOffset ? rotation * new Vector3(direction * size, 0, 0) : Vector3.zero;
EditorGUI.BeginChangeCheck();
var id = GUIUtility.GetControlID("AngleSlider".GetHashCode(), FocusType.Passive);
Vector3 cwHandle = Handles.Slider2D(id, cwPos, handleOffset, Vector3.forward, rotation * Vector3.up, rotation * Vector3.right, capSize, capFunc, Vector3.zero);
if (EditorGUI.EndChangeCheck())
{
Vector3 toCwHandle = (transform.position - cwHandle).normalized;
angle = 360 - 2 * Quaternion.Angle(Quaternion.FromToRotation(transform.up, toCwHandle), Quaternion.identity);
angle = Mathf.Round(angle * 100) / 100f;
float side = Vector3.Dot(direction * transform.right, toCwHandle);
if (side < 0)
{
if (oldAngle < 180)
angle = 0;
else
angle = 360;
}
}
if (drawLine)
Handles.DrawLine(transform.position, cwHandle);
return cwHandle;
}
private float DrawAngleHandle(Transform transform, float radius, float offset, Handles.CapFunction capLeft, Handles.CapFunction capRight, ref float angle)
{
float old = angle;
float handleOffset = HandleUtility.GetHandleSize(transform.position) * offset;
float handleSize = HandleUtility.GetHandleSize(transform.position) * k_AngleCapSize;
Quaternion rotLt = Quaternion.AngleAxis(-angle / 2, -transform.forward) * transform.rotation;
DrawAngleSlider2D(transform, rotLt, radius, handleOffset, capLeft, handleSize, true, true, true, ref angle);
Quaternion rotRt = Quaternion.AngleAxis(angle / 2, -transform.forward) * transform.rotation;
DrawAngleSlider2D(transform, rotRt, radius, handleOffset, capRight, handleSize, false, true, true, ref angle);
return angle - old;
}
private void DrawRadiusArc(Transform transform, float radius, float angle, int steps, Handles.CapFunction capFunc, float capSize, bool even)
{
Handles.DrawWireArc(transform.position, transform.forward, Quaternion.AngleAxis(180 - angle / 2, transform.forward) * -transform.up, angle, radius);
}
Handles.CapFunction GetCapFunc(Texture texture, bool isAngleHandle)
{
return (controlID, position, rotation, size, eventType) => Light2DEditorUtility.GUITextureCap(controlID, texture, position, rotation, size, eventType, isAngleHandle);
}
private void DrawAngleHandles(Light2D light)
{
var oldColor = Handles.color;
Handles.color = Color.yellow;
float outerAngle = light.pointLightOuterAngle;
float diff = DrawAngleHandle(light.transform, light.pointLightOuterRadius, k_AngleCapOffset, GetCapFunc(Styles.lightCapTopRight, true), GetCapFunc(Styles.lightCapBottomRight, true), ref outerAngle);
light.pointLightOuterAngle = outerAngle;
if (diff != 0.0f)
light.pointLightInnerAngle = Mathf.Max(0.0f, light.pointLightInnerAngle + diff);
float innerAngle = light.pointLightInnerAngle;
diff = DrawAngleHandle(light.transform, light.pointLightOuterRadius, -k_AngleCapOffset, GetCapFunc(Styles.lightCapTopLeft, true), GetCapFunc(Styles.lightCapBottomLeft, true), ref innerAngle);
light.pointLightInnerAngle = innerAngle;
if (diff != 0.0f)
light.pointLightInnerAngle = light.pointLightInnerAngle < light.pointLightOuterAngle ? light.pointLightInnerAngle : light.pointLightOuterAngle;
light.pointLightInnerAngle = Mathf.Min(light.pointLightInnerAngle, light.pointLightOuterAngle);
Handles.color = oldColor;
}
private void DrawRangeHandles(Light2D light)
{
var dummy = 0.0f;
bool radiusChanged = false;
Vector3 handlePos = Vector3.zero;
Quaternion rotLeft = Quaternion.AngleAxis(0, -light.transform.forward) * light.transform.rotation;
float handleOffset = HandleUtility.GetHandleSize(light.transform.position) * k_AngleCapOffsetSecondary;
float handleSize = HandleUtility.GetHandleSize(light.transform.position) * k_AngleCapSize;
var oldColor = Handles.color;
Handles.color = Color.yellow;
float outerRadius = light.pointLightOuterRadius;
EditorGUI.BeginChangeCheck();
Vector3 returnPos = DrawAngleSlider2D(light.transform, rotLeft, outerRadius, -handleOffset, GetCapFunc(Styles.lightCapUp, false), handleSize, false, false, false, ref dummy);
if (EditorGUI.EndChangeCheck())
{
var vec = (returnPos - light.transform.position).normalized;
light.transform.up = new Vector3(vec.x, vec.y, 0);
outerRadius = (returnPos - light.transform.position).magnitude;
outerRadius = outerRadius + handleOffset;
radiusChanged = true;
}
DrawRadiusArc(light.transform, light.pointLightOuterRadius, light.pointLightOuterAngle, 0, Handles.DotHandleCap, k_RangeCapSize, false);
Handles.color = Color.gray;
float innerRadius = light.pointLightInnerRadius;
EditorGUI.BeginChangeCheck();
returnPos = DrawAngleSlider2D(light.transform, rotLeft, innerRadius, handleOffset, GetCapFunc(Styles.lightCapDown, false), handleSize, true, false, false, ref dummy);
if (EditorGUI.EndChangeCheck())
{
innerRadius = (returnPos - light.transform.position).magnitude;
innerRadius = innerRadius - handleOffset;
radiusChanged = true;
}
DrawRadiusArc(light.transform, light.pointLightInnerRadius, light.pointLightOuterAngle, 0, Handles.SphereHandleCap, k_InnerRangeCapSize, false);
Handles.color = oldColor;
if (radiusChanged)
{
light.pointLightInnerRadius = (outerRadius < innerRadius) ? outerRadius : innerRadius;
light.pointLightOuterRadius = (innerRadius > outerRadius) ? innerRadius : outerRadius;
}
}
void OnSceneGUI()
{
var light = target as Light2D;
if (light == null)
return;
Transform t = light.transform;
switch (light.lightType)
{
case Light2D.LightType.Point:
{
Undo.RecordObject(light.transform, "Edit Point Light Transform");
Undo.RecordObject(light, "Edit Point Light");
DrawRangeHandles(light);
DrawAngleHandles(light);
if (GUI.changed)
EditorUtility.SetDirty(light);
}
break;
case Light2D.LightType.Sprite:
{
var cookieSprite = light.lightCookieSprite;
if (cookieSprite != null)
{
Vector3 min = cookieSprite.bounds.min;
Vector3 max = cookieSprite.bounds.max;
Vector3 v0 = t.TransformPoint(new Vector3(min.x, min.y));
Vector3 v1 = t.TransformPoint(new Vector3(max.x, min.y));
Vector3 v2 = t.TransformPoint(new Vector3(max.x, max.y));
Vector3 v3 = t.TransformPoint(new Vector3(min.x, max.y));
Handles.DrawLine(v0, v1);
Handles.DrawLine(v1, v2);
Handles.DrawLine(v2, v3);
Handles.DrawLine(v3, v0);
}
}
break;
case Light2D.LightType.Freeform:
{
// Draw the falloff shape's outline
List<Vector2> falloffShape = light.GetFalloffShape();
Handles.color = Color.white;
for (int i = 0; i < falloffShape.Count - 1; ++i)
{
Handles.DrawLine(t.TransformPoint(falloffShape[i]), t.TransformPoint(falloffShape[i + 1]));
}
if (falloffShape.Count > 0)
Handles.DrawLine(t.TransformPoint(falloffShape[falloffShape.Count - 1]), t.TransformPoint(falloffShape[0]));
for (int i = 0; i < light.shapePath.Length - 1; ++i)
{
Handles.DrawLine(t.TransformPoint(light.shapePath[i]),
t.TransformPoint(light.shapePath[i + 1]));
}
if (light.shapePath.Length > 0)
Handles.DrawLine(t.TransformPoint(light.shapePath[light.shapePath.Length - 1]), t.TransformPoint(light.shapePath[0]));
}
break;
}
}
public override void OnInspectorGUI()
{
var meshChanged = false;
serializedObject.Update();
UniversalRenderPipelineAsset asset = UniversalRenderPipeline.asset;
if (asset != null)
{
if (!Light2DEditorUtility.IsUsing2DRenderer())
{
EditorGUILayout.HelpBox(Styles.asset2DUnassignedWarning);
}
else
{
if (m_LightType.intValue != (int)Light2D.DeprecatedLightType.Parametric)
meshChanged = DrawLightCommon();
switch (m_LightType.intValue)
{
case (int)Light2D.LightType.Point:
{
DrawSpotLight(serializedObject);
}
break;
case (int)Light2D.LightType.Freeform:
{
DrawShapeLight(serializedObject);
}
break;
case (int)Light2D.LightType.Sprite:
{
DrawSpriteLight(serializedObject);
}
break;
case (int)Light2D.LightType.Global:
{
DrawGlobalLight(serializedObject);
}
break;
case (int)Light2D.DeprecatedLightType.Parametric:
{
DrawParametricDeprecated(serializedObject);
}
break;
}
AnalyticsTrackChanges(serializedObject);
if (serializedObject.ApplyModifiedProperties())
{
if (meshChanged)
lightObject.UpdateMesh();
}
}
}
else
{
EditorGUILayout.HelpBox(Styles.renderPipelineUnassignedWarning);
if (meshChanged)
lightObject.UpdateMesh();
}
}
}
internal class Light2DPostProcess : AssetPostprocessor
{
void OnPostprocessSprites(Texture2D texture, Sprite[] sprites)
{
var lights = Resources.FindObjectsOfTypeAll<Light2D>().Where(x => x.lightType == Light2D.LightType.Sprite && x.lightCookieSprite == null);
foreach (var light in lights)
light.MarkForUpdate();
}
}
}