2021-09-09 20:42:29 -04:00

1322 lines
76 KiB
C#

using System;
using System.Linq.Expressions;
using UnityEngine;
using UnityEngine.Rendering.HighDefinition;
using UnityEngine.Rendering;
namespace UnityEditor.Rendering.HighDefinition
{
using CED = CoreEditorDrawer<SerializedHDLight>;
static partial class HDLightUI
{
public static class ScalableSettings
{
public static IntScalableSetting ShadowResolution(HDLightType lightType, HDRenderPipelineAsset hdrp)
{
switch (lightType)
{
case HDLightType.Directional: return HDAdditionalLightData.ScalableSettings.ShadowResolutionDirectional(hdrp);
case HDLightType.Point: return HDAdditionalLightData.ScalableSettings.ShadowResolutionPunctual(hdrp);
case HDLightType.Spot: return HDAdditionalLightData.ScalableSettings.ShadowResolutionPunctual(hdrp);
case HDLightType.Area: return HDAdditionalLightData.ScalableSettings.ShadowResolutionArea(hdrp);
default: throw new ArgumentOutOfRangeException(nameof(lightType));
}
}
}
enum ShadowmaskMode
{
Shadowmask,
DistanceShadowmask
}
enum Expandable
{
General = 1 << 0,
Shape = 1 << 1,
Emission = 1 << 2,
Volumetric = 1 << 3,
Shadows = 1 << 4,
ShadowMap = 1 << 5,
ContactShadow = 1 << 6,
BakedShadow = 1 << 7,
ShadowQuality = 1 << 8,
CelestialBody = 1 << 9,
}
enum AdvancedMode
{
General = 1 << 0,
Shape = 1 << 1,
Emission = 1 << 2,
Shadow = 1 << 3,
}
const float k_MinLightSize = 0.01f; // Provide a small size of 1cm for line light
readonly static ExpandedState<Expandable, Light> k_ExpandedState = new ExpandedState<Expandable, Light>(~(-1), "HDRP");
readonly static LightUnitSliderUIDrawer k_LightUnitSliderUIDrawer = new LightUnitSliderUIDrawer();
public static readonly CED.IDrawer Inspector;
static bool GetAdvanced(AdvancedMode mask, SerializedHDLight serialized, Editor owner)
{
return (serialized.showAdditionalSettings.intValue & (int)mask) != 0;
}
static void SetAdvanced(AdvancedMode mask, bool value, SerializedHDLight serialized, Editor owner)
{
if (value)
{
serialized.showAdditionalSettings.intValue |= (int)mask;
}
else
{
serialized.showAdditionalSettings.intValue &= ~(int)mask;
}
}
static void SwitchAdvanced(AdvancedMode mask, SerializedHDLight serialized, Editor owner)
{
if ((serialized.showAdditionalSettings.intValue & (int)mask) != 0)
{
serialized.showAdditionalSettings.intValue &= ~(int)mask;
}
else
{
serialized.showAdditionalSettings.intValue |= (int)mask;
}
}
static Action<GUIContent, SerializedProperty, LightEditor.Settings> SliderWithTexture;
static HDLightUI()
{
Inspector = CED.Group(
CED.AdvancedFoldoutGroup(s_Styles.generalHeader, Expandable.General, k_ExpandedState,
(serialized, owner) => GetAdvanced(AdvancedMode.General, serialized, owner),
(serialized, owner) => SwitchAdvanced(AdvancedMode.General, serialized, owner),
DrawGeneralContent,
DrawGeneralAdvancedContent
),
CED.FoldoutGroup(s_Styles.shapeHeader, Expandable.Shape, k_ExpandedState, DrawShapeContent),
CED.Conditional((serialized, owner) => serialized.type == HDLightType.Directional && !serialized.settings.isCompletelyBaked,
CED.FoldoutGroup(s_Styles.celestialBodyHeader, Expandable.CelestialBody, k_ExpandedState, DrawCelestialBodyContent)),
CED.AdvancedFoldoutGroup(s_Styles.emissionHeader, Expandable.Emission, k_ExpandedState,
(serialized, owner) => GetAdvanced(AdvancedMode.Emission, serialized, owner),
(serialized, owner) => SwitchAdvanced(AdvancedMode.Emission, serialized, owner),
DrawEmissionContent,
DrawEmissionAdvancedContent
),
CED.Conditional((serialized, owner) => serialized.type != HDLightType.Area && !serialized.settings.isCompletelyBaked,
CED.FoldoutGroup(s_Styles.volumetricHeader, Expandable.Volumetric, k_ExpandedState, DrawVolumetric)),
CED.Conditional((serialized, owner) =>
{
HDLightType type = serialized.type;
return type != HDLightType.Area || type == HDLightType.Area && serialized.areaLightShape != AreaLightShape.Tube;
},
CED.TernaryConditional((serialized, owner) => !serialized.settings.isCompletelyBaked,
CED.AdvancedFoldoutGroup(s_Styles.shadowHeader, Expandable.Shadows, k_ExpandedState,
(serialized, owner) => GetAdvanced(AdvancedMode.Shadow, serialized, owner),
(serialized, owner) => SwitchAdvanced(AdvancedMode.Shadow, serialized, owner),
CED.Group(
CED.FoldoutGroup(s_Styles.shadowMapSubHeader, Expandable.ShadowMap, k_ExpandedState, FoldoutOption.SubFoldout | FoldoutOption.Indent | FoldoutOption.NoSpaceAtEnd, DrawShadowMapContent),
CED.Conditional((serialized, owner) => GetAdvanced(AdvancedMode.Shadow, serialized, owner) && k_ExpandedState[Expandable.ShadowMap],
CED.Group(GroupOption.Indent, DrawShadowMapAdvancedContent)),
CED.space,
CED.Conditional((serialized, owner) => GetAdvanced(AdvancedMode.Shadow, serialized, owner) && HasShadowQualitySettingsUI(HDShadowFilteringQuality.High, serialized, owner),
CED.FoldoutGroup(s_Styles.highShadowQualitySubHeader, Expandable.ShadowQuality, k_ExpandedState, FoldoutOption.SubFoldout | FoldoutOption.Indent, DrawHighShadowSettingsContent)),
CED.Conditional((serialized, owner) => HasShadowQualitySettingsUI(HDShadowFilteringQuality.Medium, serialized, owner),
CED.FoldoutGroup(s_Styles.mediumShadowQualitySubHeader, Expandable.ShadowQuality, k_ExpandedState, FoldoutOption.SubFoldout | FoldoutOption.Indent, DrawMediumShadowSettingsContent)),
CED.Conditional((serialized, owner) => HasShadowQualitySettingsUI(HDShadowFilteringQuality.Low, serialized, owner),
CED.FoldoutGroup(s_Styles.lowShadowQualitySubHeader, Expandable.ShadowQuality, k_ExpandedState, FoldoutOption.SubFoldout | FoldoutOption.Indent, DrawLowShadowSettingsContent)),
CED.Conditional((serialized, owner) => serialized.type != HDLightType.Area,
CED.FoldoutGroup(s_Styles.contactShadowsSubHeader, Expandable.ContactShadow, k_ExpandedState, FoldoutOption.SubFoldout | FoldoutOption.Indent | FoldoutOption.NoSpaceAtEnd, DrawContactShadowsContent)
)
),
CED.noop //will only add parameter in first sub header
),
CED.FoldoutGroup(s_Styles.shadowHeader, Expandable.Shadows, k_ExpandedState,
CED.FoldoutGroup(s_Styles.bakedShadowsSubHeader, Expandable.BakedShadow, k_ExpandedState, FoldoutOption.SubFoldout | FoldoutOption.Indent | FoldoutOption.NoSpaceAtEnd, DrawBakedShadowsContent))
)
)
);
//quicker than standard reflection as it is compiled
var paramLabel = Expression.Parameter(typeof(GUIContent), "label");
var paramProperty = Expression.Parameter(typeof(SerializedProperty), "property");
var paramSettings = Expression.Parameter(typeof(LightEditor.Settings), "settings");
System.Reflection.MethodInfo sliderWithTextureInfo = typeof(EditorGUILayout)
.GetMethod(
"SliderWithTexture",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static,
null,
System.Reflection.CallingConventions.Any,
new[] { typeof(GUIContent), typeof(SerializedProperty), typeof(float), typeof(float), typeof(float), typeof(Texture2D), typeof(GUILayoutOption[]) },
null);
var sliderWithTextureCall = Expression.Call(
sliderWithTextureInfo,
paramLabel,
paramProperty,
Expression.Constant((float)typeof(LightEditor.Settings).GetField("kMinKelvin", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic).GetRawConstantValue()),
Expression.Constant((float)typeof(LightEditor.Settings).GetField("kMaxKelvin", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic).GetRawConstantValue()),
Expression.Constant((float)typeof(LightEditor.Settings).GetField("kSliderPower", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic).GetRawConstantValue()),
Expression.Field(paramSettings, typeof(LightEditor.Settings).GetField("m_KelvinGradientTexture", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)),
Expression.Constant(null, typeof(GUILayoutOption[])));
var lambda = Expression.Lambda<Action<GUIContent, SerializedProperty, LightEditor.Settings>>(sliderWithTextureCall, paramLabel, paramProperty, paramSettings);
SliderWithTexture = lambda.Compile();
}
static void DrawGeneralContent(SerializedHDLight serialized, Editor owner)
{
EditorGUI.BeginChangeCheck();
Rect lineRect = EditorGUILayout.GetControlRect();
HDLightType lightType = serialized.type;
HDLightType updatedLightType;
//Partial support for prefab. There is no way to fully support it at the moment.
//Missing support on the Apply and Revert contextual menu on Label for Prefab overrides. They need to be done two times.
//(This will continue unless we remove AdditionalDatas)
using (new SerializedHDLight.LightTypeEditionScope(lineRect, s_Styles.shape, serialized))
{
EditorGUI.showMixedValue = lightType == (HDLightType)(-1);
int index = Array.FindIndex((HDLightType[])Enum.GetValues(typeof(HDLightType)), x => x == lightType);
updatedLightType = (HDLightType)EditorGUI.Popup(lineRect, s_Styles.shape, index, s_Styles.shapeNames);
}
if (EditorGUI.EndChangeCheck())
{
serialized.type = updatedLightType; //also register undo
if (updatedLightType == HDLightType.Area)
{
switch (serialized.areaLightShape)
{
case AreaLightShape.Rectangle:
serialized.shapeWidth.floatValue = Mathf.Max(serialized.shapeWidth.floatValue, k_MinLightSize);
serialized.shapeHeight.floatValue = Mathf.Max(serialized.shapeHeight.floatValue, k_MinLightSize);
break;
case AreaLightShape.Tube:
serialized.settings.shadowsType.SetEnumValue(LightShadows.None);
serialized.shapeWidth.floatValue = Mathf.Max(serialized.shapeWidth.floatValue, k_MinLightSize);
break;
case AreaLightShape.Disc:
//nothing to do
break;
case (AreaLightShape)(-1):
// don't do anything, this is just to handle multi selection
break;
}
}
UpdateLightIntensityUnit(serialized, owner);
// For GI we need to detect any change on additional data and call SetLightDirty + For intensity we need to detect light shape change
serialized.needUpdateAreaLightEmissiveMeshComponents = true;
serialized.FetchAreaLightEmissiveMeshComponents();
SetLightsDirty(owner); // Should be apply only to parameter that's affect GI, but make the code cleaner
}
EditorGUI.showMixedValue = false;
// Draw the mode, for Tube and Disc lights, there is only one choice, so we can disable the enum.
using (new EditorGUI.DisabledScope(serialized.areaLightShape == AreaLightShape.Tube || serialized.areaLightShape == AreaLightShape.Disc))
serialized.settings.DrawLightmapping();
if (updatedLightType == HDLightType.Area)
{
switch (serialized.areaLightShape)
{
case AreaLightShape.Tube:
if (serialized.settings.isBakedOrMixed)
EditorGUILayout.HelpBox("Tube Area Lights are realtime only.", MessageType.Error);
break;
case AreaLightShape.Disc:
if (!serialized.settings.isCompletelyBaked)
EditorGUILayout.HelpBox("Disc Area Lights are baked only.", MessageType.Error);
break;
}
}
}
static void DrawGeneralAdvancedContent(SerializedHDLight serialized, Editor owner)
{
using (new EditorGUI.DisabledScope(!HDUtils.hdrpSettings.supportLightLayers))
{
using (var change = new EditorGUI.ChangeCheckScope())
{
EditorGUILayout.PropertyField(serialized.lightlayersMask, s_Styles.lightLayer);
// If we're not in decoupled mode for light layers, we sync light with shadow layers:
if (serialized.linkLightLayers.boolValue && change.changed && !serialized.lightlayersMask.hasMultipleDifferentValues)
SyncLightAndShadowLayers(serialized, owner);
}
}
}
static void DrawShapeContent(SerializedHDLight serialized, Editor owner)
{
EditorGUI.BeginChangeCheck(); // For GI we need to detect any change on additional data and call SetLightDirty + For intensity we need to detect light shape change
// LightShape is HD specific, it need to drive LightType from the original LightType
// when it make sense, so the GI is still in sync with the light shape
switch (serialized.type)
{
case HDLightType.Directional:
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(serialized.angularDiameter, s_Styles.angularDiameter);
if (EditorGUI.EndChangeCheck())
{
serialized.angularDiameter.floatValue = Mathf.Clamp(serialized.angularDiameter.floatValue, 0, 90);
serialized.settings.bakedShadowAngleProp.floatValue = serialized.angularDiameter.floatValue;
}
break;
case HDLightType.Point:
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(serialized.shapeRadius, s_Styles.lightRadius);
if (EditorGUI.EndChangeCheck())
{
//Also affect baked shadows
serialized.settings.bakedShadowRadiusProp.floatValue = serialized.shapeRadius.floatValue;
}
break;
case HDLightType.Spot:
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(serialized.spotLightShape, s_Styles.spotLightShape);
if (EditorGUI.EndChangeCheck())
{
UpdateLightIntensityUnit(serialized, owner);
}
switch (serialized.spotLightShape.GetEnumValue<SpotLightShape>())
{
case SpotLightShape.Box:
// Box directional light.
EditorGUILayout.PropertyField(serialized.shapeWidth, s_Styles.shapeWidthBox);
EditorGUILayout.PropertyField(serialized.shapeHeight, s_Styles.shapeHeightBox);
break;
case SpotLightShape.Cone:
// Cone spot projector
EditorGUI.BeginChangeCheck();
EditorGUILayout.Slider(serialized.settings.spotAngle, HDAdditionalLightData.k_MinSpotAngle, HDAdditionalLightData.k_MaxSpotAngle, s_Styles.outterAngle);
if (EditorGUI.EndChangeCheck())
{
serialized.customSpotLightShadowCone.floatValue = Math.Min(serialized.customSpotLightShadowCone.floatValue, serialized.settings.spotAngle.floatValue);
}
EditorGUILayout.PropertyField(serialized.spotInnerPercent, s_Styles.spotInnerPercent);
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(serialized.shapeRadius, s_Styles.lightRadius);
if (EditorGUI.EndChangeCheck())
{
//Also affect baked shadows
serialized.settings.bakedShadowRadiusProp.floatValue = serialized.shapeRadius.floatValue;
}
break;
case SpotLightShape.Pyramid:
// pyramid spot projector
EditorGUI.BeginChangeCheck();
serialized.settings.DrawSpotAngle();
if (EditorGUI.EndChangeCheck())
{
serialized.customSpotLightShadowCone.floatValue = Math.Min(serialized.customSpotLightShadowCone.floatValue, serialized.settings.spotAngle.floatValue);
}
EditorGUILayout.Slider(serialized.aspectRatio, HDAdditionalLightData.k_MinAspectRatio, HDAdditionalLightData.k_MaxAspectRatio, s_Styles.aspectRatioPyramid);
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(serialized.shapeRadius, s_Styles.lightRadius);
if (EditorGUI.EndChangeCheck())
{
//Also affect baked shadows
serialized.settings.bakedShadowRadiusProp.floatValue = serialized.shapeRadius.floatValue;
}
break;
case (SpotLightShape)(-1): //multiple different values
using (new EditorGUI.DisabledScope(true))
EditorGUILayout.LabelField("Multiple different spot Shapes in selection");
break;
default:
Debug.Assert(false, "Not implemented spot light shape");
break;
}
break;
case HDLightType.Area:
EditorGUI.BeginChangeCheck();
Rect lineRect = EditorGUILayout.GetControlRect();
AreaLightShape updatedAreaLightShape;
//Partial support for prefab. There is no way to fully support it at the moment.
//Missing support on the Apply and Revert contextual menu on Label for Prefab overrides. They need to be done two times.
//(This will continue unless we have our own handling for Disc or remove AdditionalDatas)
using (new SerializedHDLight.AreaLightShapeEditionScope(lineRect, s_Styles.shape, serialized))
{
AreaLightShape areaLightShape = serialized.areaLightShape;
EditorGUI.showMixedValue = areaLightShape == (AreaLightShape)(-1);
int index = Array.FindIndex((AreaLightShape[])Enum.GetValues(typeof(AreaLightShape)), x => x == areaLightShape);
updatedAreaLightShape = (AreaLightShape)EditorGUI.Popup(lineRect, s_Styles.areaLightShape, index, s_Styles.areaShapeNames);
}
if (EditorGUI.EndChangeCheck())
{
serialized.areaLightShape = updatedAreaLightShape; //also register undo
UpdateLightIntensityUnit(serialized, owner);
}
EditorGUI.showMixedValue = false;
switch (updatedAreaLightShape)
{
case AreaLightShape.Rectangle:
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(serialized.shapeWidth, s_Styles.shapeWidthRect);
EditorGUILayout.PropertyField(serialized.shapeHeight, s_Styles.shapeHeightRect);
if (ShaderConfig.s_BarnDoor == 1)
{
EditorGUILayout.PropertyField(serialized.barnDoorAngle, s_Styles.barnDoorAngle);
EditorGUILayout.PropertyField(serialized.barnDoorLength, s_Styles.barnDoorLength);
}
if (EditorGUI.EndChangeCheck())
{
serialized.settings.areaSizeX.floatValue = serialized.shapeWidth.floatValue;
serialized.settings.areaSizeY.floatValue = serialized.shapeHeight.floatValue;
if (ShaderConfig.s_BarnDoor == 1)
{
serialized.barnDoorAngle.floatValue = Mathf.Clamp(serialized.barnDoorAngle.floatValue, 0.0f, 90.0f);
serialized.barnDoorLength.floatValue = Mathf.Clamp(serialized.barnDoorLength.floatValue, 0.0f, float.MaxValue);
}
}
break;
case AreaLightShape.Tube:
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(serialized.shapeWidth, s_Styles.shapeWidthTube);
if (EditorGUI.EndChangeCheck())
{
// Fake line with a small rectangle in vanilla unity for GI
serialized.settings.areaSizeX.floatValue = serialized.shapeWidth.floatValue;
serialized.settings.areaSizeY.floatValue = k_MinLightSize;
}
break;
case AreaLightShape.Disc:
//draw the built-in area light control at the moment as everything is handled by built-in
serialized.settings.DrawArea();
serialized.displayAreaLightEmissiveMesh.boolValue = false; //force deactivate emissive mesh for Disc (not supported)
break;
case (AreaLightShape)(-1): //multiple different values
using (new EditorGUI.DisabledScope(true))
EditorGUILayout.LabelField("Multiple different area Shapes in selection");
break;
default:
Debug.Assert(false, "Not implemented area light shape");
break;
}
break;
case (HDLightType)(-1): //multiple different values
using (new EditorGUI.DisabledScope(true))
EditorGUILayout.LabelField("Multiple different Types in selection");
break;
default:
Debug.Assert(false, "Not implemented light type");
break;
}
if (EditorGUI.EndChangeCheck())
{
// Light size must be non-zero, else we get NaNs.
serialized.shapeWidth.floatValue = Mathf.Max(serialized.shapeWidth.floatValue, k_MinLightSize);
serialized.shapeHeight.floatValue = Mathf.Max(serialized.shapeHeight.floatValue, k_MinLightSize);
serialized.shapeRadius.floatValue = Mathf.Max(serialized.shapeRadius.floatValue, 0.0f);
serialized.needUpdateAreaLightEmissiveMeshComponents = true;
SetLightsDirty(owner); // Should be apply only to parameter that's affect GI, but make the code cleaner
}
}
static void DrawCelestialBodyContent(SerializedHDLight serialized, Editor owner)
{
EditorGUI.BeginChangeCheck();
{
EditorGUILayout.PropertyField(serialized.interactsWithSky, s_Styles.interactsWithSky);
using (new EditorGUI.DisabledScope(!serialized.interactsWithSky.boolValue))
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(serialized.flareSize, s_Styles.flareSize);
EditorGUILayout.PropertyField(serialized.flareFalloff, s_Styles.flareFalloff);
EditorGUILayout.PropertyField(serialized.flareTint, s_Styles.flareTint);
EditorGUILayout.PropertyField(serialized.surfaceTexture, s_Styles.surfaceTexture);
EditorGUILayout.PropertyField(serialized.surfaceTint, s_Styles.surfaceTint);
EditorGUILayout.PropertyField(serialized.distance, s_Styles.distance);
EditorGUI.indentLevel--;
}
}
if (EditorGUI.EndChangeCheck())
{
// Clamp the value and also affect baked shadows.
serialized.flareSize.floatValue = Mathf.Clamp(serialized.flareSize.floatValue, 0, 90);
serialized.flareFalloff.floatValue = Mathf.Max(serialized.flareFalloff.floatValue, 0);
serialized.distance.floatValue = Mathf.Max(serialized.distance.floatValue, 0);
}
}
static void UpdateLightIntensityUnit(SerializedHDLight serialized, Editor owner)
{
HDLightType lightType = serialized.type;
// Box are local directional light
if (lightType == HDLightType.Directional ||
(lightType == HDLightType.Spot && (serialized.spotLightShape.GetEnumValue<SpotLightShape>() == SpotLightShape.Box)))
{
serialized.lightUnit.SetEnumValue((LightUnit)DirectionalLightUnit.Lux);
// We need to reset luxAtDistance to neutral when changing to (local) directional light, otherwise first display value ins't correct
serialized.luxAtDistance.floatValue = 1.0f;
}
}
static void DrawLightIntensityUnitPopup(Rect rect, SerializedHDLight serialized, Editor owner)
{
LightUnit selectedLightUnit;
LightUnit oldLigthUnit = serialized.lightUnit.GetEnumValue<LightUnit>();
EditorGUI.showMixedValue = serialized.lightUnit.hasMultipleDifferentValues;
EditorGUI.BeginChangeCheck();
EditorGUI.BeginProperty(rect, GUIContent.none, serialized.lightUnit);
switch (serialized.type)
{
case HDLightType.Directional:
selectedLightUnit = (LightUnit)EditorGUI.EnumPopup(rect, (DirectionalLightUnit)serialized.lightUnit.GetEnumValue<DirectionalLightUnit>());
break;
case HDLightType.Point:
selectedLightUnit = (LightUnit)EditorGUI.EnumPopup(rect, (PunctualLightUnit)serialized.lightUnit.GetEnumValue<PunctualLightUnit>());
break;
case HDLightType.Spot:
if (serialized.spotLightShape.GetEnumValue<SpotLightShape>() == SpotLightShape.Box)
selectedLightUnit = (LightUnit)EditorGUI.EnumPopup(rect, (DirectionalLightUnit)serialized.lightUnit.GetEnumValue<DirectionalLightUnit>());
else
selectedLightUnit = (LightUnit)EditorGUI.EnumPopup(rect, (PunctualLightUnit)serialized.lightUnit.GetEnumValue<PunctualLightUnit>());
break;
default:
selectedLightUnit = (LightUnit)EditorGUI.EnumPopup(rect, (AreaLightUnit)serialized.lightUnit.GetEnumValue<AreaLightUnit>());
break;
}
EditorGUI.EndProperty();
EditorGUI.showMixedValue = false;
if (EditorGUI.EndChangeCheck())
{
ConvertLightIntensity(oldLigthUnit, selectedLightUnit, serialized, owner);
serialized.lightUnit.SetEnumValue(selectedLightUnit);
}
}
internal static void ConvertLightIntensity(LightUnit oldLightUnit, LightUnit newLightUnit, SerializedHDLight serialized, Editor owner)
{
serialized.intensity.floatValue = ConvertLightIntensity(oldLightUnit, newLightUnit, serialized, owner, serialized.intensity.floatValue);
}
internal static float ConvertLightIntensity(LightUnit oldLightUnit, LightUnit newLightUnit, SerializedHDLight serialized, Editor owner, float intensity)
{
Light light = (Light)owner.target;
// For punctual lights
HDLightType lightType = serialized.type;
switch (lightType)
{
case HDLightType.Directional:
case HDLightType.Point:
case HDLightType.Spot:
// Lumen ->
if (oldLightUnit == LightUnit.Lumen && newLightUnit == LightUnit.Candela)
intensity = LightUtils.ConvertPunctualLightLumenToCandela(lightType, intensity, light.intensity, serialized.enableSpotReflector.boolValue);
else if (oldLightUnit == LightUnit.Lumen && newLightUnit == LightUnit.Lux)
intensity = LightUtils.ConvertPunctualLightLumenToLux(lightType, intensity, light.intensity, serialized.enableSpotReflector.boolValue,
serialized.luxAtDistance.floatValue);
else if (oldLightUnit == LightUnit.Lumen && newLightUnit == LightUnit.Ev100)
intensity = LightUtils.ConvertPunctualLightLumenToEv(lightType, intensity, light.intensity, serialized.enableSpotReflector.boolValue);
// Candela ->
else if (oldLightUnit == LightUnit.Candela && newLightUnit == LightUnit.Lumen)
intensity = LightUtils.ConvertPunctualLightCandelaToLumen(lightType, serialized.spotLightShape.GetEnumValue<SpotLightShape>(), intensity, serialized.enableSpotReflector.boolValue,
light.spotAngle, serialized.aspectRatio.floatValue);
else if (oldLightUnit == LightUnit.Candela && newLightUnit == LightUnit.Lux)
intensity = LightUtils.ConvertCandelaToLux(intensity, serialized.luxAtDistance.floatValue);
else if (oldLightUnit == LightUnit.Candela && newLightUnit == LightUnit.Ev100)
intensity = LightUtils.ConvertCandelaToEv(intensity);
// Lux ->
else if (oldLightUnit == LightUnit.Lux && newLightUnit == LightUnit.Lumen)
intensity = LightUtils.ConvertPunctualLightLuxToLumen(lightType, serialized.spotLightShape.GetEnumValue<SpotLightShape>(), intensity, serialized.enableSpotReflector.boolValue,
light.spotAngle, serialized.aspectRatio.floatValue, serialized.luxAtDistance.floatValue);
else if (oldLightUnit == LightUnit.Lux && newLightUnit == LightUnit.Candela)
intensity = LightUtils.ConvertLuxToCandela(intensity, serialized.luxAtDistance.floatValue);
else if (oldLightUnit == LightUnit.Lux && newLightUnit == LightUnit.Ev100)
intensity = LightUtils.ConvertLuxToEv(intensity, serialized.luxAtDistance.floatValue);
// EV100 ->
else if (oldLightUnit == LightUnit.Ev100 && newLightUnit == LightUnit.Lumen)
intensity = LightUtils.ConvertPunctualLightEvToLumen(lightType, serialized.spotLightShape.GetEnumValue<SpotLightShape>(), intensity, serialized.enableSpotReflector.boolValue,
light.spotAngle, serialized.aspectRatio.floatValue);
else if (oldLightUnit == LightUnit.Ev100 && newLightUnit == LightUnit.Candela)
intensity = LightUtils.ConvertEvToCandela(intensity);
else if (oldLightUnit == LightUnit.Ev100 && newLightUnit == LightUnit.Lux)
intensity = LightUtils.ConvertEvToLux(intensity, serialized.luxAtDistance.floatValue);
break;
case HDLightType.Area:
if (oldLightUnit == LightUnit.Lumen && newLightUnit == LightUnit.Nits)
intensity = LightUtils.ConvertAreaLightLumenToLuminance(serialized.areaLightShape, intensity, serialized.shapeWidth.floatValue, serialized.shapeHeight.floatValue);
if (oldLightUnit == LightUnit.Nits && newLightUnit == LightUnit.Lumen)
intensity = LightUtils.ConvertAreaLightLuminanceToLumen(serialized.areaLightShape, intensity, serialized.shapeWidth.floatValue, serialized.shapeHeight.floatValue);
if (oldLightUnit == LightUnit.Nits && newLightUnit == LightUnit.Ev100)
intensity = LightUtils.ConvertLuminanceToEv(intensity);
if (oldLightUnit == LightUnit.Ev100 && newLightUnit == LightUnit.Nits)
intensity = LightUtils.ConvertEvToLuminance(intensity);
if (oldLightUnit == LightUnit.Ev100 && newLightUnit == LightUnit.Lumen)
intensity = LightUtils.ConvertAreaLightEvToLumen(serialized.areaLightShape, intensity, serialized.shapeWidth.floatValue, serialized.shapeHeight.floatValue);
if (oldLightUnit == LightUnit.Lumen && newLightUnit == LightUnit.Ev100)
intensity = LightUtils.ConvertAreaLightLumenToEv(serialized.areaLightShape, intensity, serialized.shapeWidth.floatValue, serialized.shapeHeight.floatValue);
break;
default:
case (HDLightType)(-1): // multiple different values
break; // do nothing
}
return intensity;
}
static void DrawLightIntensityGUILayout(SerializedHDLight serialized, Editor owner)
{
// Match const defined in EditorGUI.cs
const int k_IndentPerLevel = 15;
const int k_ValueUnitSeparator = 2;
const int k_UnitWidth = 100;
float indent = k_IndentPerLevel * EditorGUI.indentLevel;
Rect lineRect = EditorGUILayout.GetControlRect();
Rect labelRect = lineRect;
labelRect.width = EditorGUIUtility.labelWidth;
// Expand to reach both lines of the intensity field.
var interlineOffset = EditorGUIUtility.singleLineHeight + 2f;
labelRect.height += interlineOffset;
//handling of prefab overrides in a parent label
GUIContent parentLabel = s_Styles.lightIntensity;
parentLabel = EditorGUI.BeginProperty(labelRect, parentLabel, serialized.lightUnit);
parentLabel = EditorGUI.BeginProperty(labelRect, parentLabel, serialized.intensity);
{
// Restore the original rect for actually drawing the label.
labelRect.height -= interlineOffset;
EditorGUI.LabelField(labelRect, parentLabel);
}
EditorGUI.EndProperty();
EditorGUI.EndProperty();
// Draw the light unit slider + icon + tooltip
Rect lightUnitSliderRect = lineRect; // TODO: Move the value and unit rects to new line
lightUnitSliderRect.x += EditorGUIUtility.labelWidth + k_ValueUnitSeparator;
lightUnitSliderRect.width -= EditorGUIUtility.labelWidth + k_ValueUnitSeparator;
var lightType = serialized.type;
var lightUnit = serialized.lightUnit.GetEnumValue<LightUnit>();
k_LightUnitSliderUIDrawer.SetSerializedObject(serialized.serializedObject);
k_LightUnitSliderUIDrawer.Draw(lightType, lightUnit, serialized.intensity, lightUnitSliderRect, serialized, owner);
// We use PropertyField to draw the value to keep the handle at left of the field
// This will apply the indent again thus we need to remove it time for alignment
Rect valueRect = EditorGUILayout.GetControlRect();
labelRect.width = EditorGUIUtility.labelWidth;
valueRect.width += indent - k_ValueUnitSeparator - k_UnitWidth;
Rect unitRect = valueRect;
unitRect.x += valueRect.width - indent + k_ValueUnitSeparator;
unitRect.width = k_UnitWidth + .5f;
// Draw the unit textfield
EditorGUI.BeginChangeCheck();
EditorGUI.PropertyField(valueRect, serialized.intensity, s_Styles.empty);
DrawLightIntensityUnitPopup(unitRect, serialized, owner);
if (EditorGUI.EndChangeCheck())
{
serialized.intensity.floatValue = Mathf.Max(serialized.intensity.floatValue, 0.0f);
}
}
static void DrawEmissionContent(SerializedHDLight serialized, Editor owner)
{
using (var changes = new EditorGUI.ChangeCheckScope())
{
if (GraphicsSettings.lightsUseLinearIntensity && GraphicsSettings.lightsUseColorTemperature)
{
// Use the color temperature bool to create a popup dropdown to choose between the two modes.
var colorTemperaturePopupValue = Convert.ToInt32(serialized.settings.useColorTemperature.boolValue);
var lightAppearanceOptions = new[] { "Color", "Filter and Temperature" };
colorTemperaturePopupValue = EditorGUILayout.Popup(s_Styles.lightAppearance, colorTemperaturePopupValue, lightAppearanceOptions);
serialized.settings.useColorTemperature.boolValue = Convert.ToBoolean(colorTemperaturePopupValue);
if (serialized.settings.useColorTemperature.boolValue)
{
EditorGUI.indentLevel += 1;
EditorGUILayout.PropertyField(serialized.settings.color, s_Styles.colorFilter);
// Light unit slider
const int k_ValueUnitSeparator = 2;
var lineRect = EditorGUILayout.GetControlRect();
var labelRect = lineRect;
labelRect.width = EditorGUIUtility.labelWidth;
EditorGUI.LabelField(labelRect, s_Styles.colorTemperature);
var temperatureSliderRect = lineRect;
temperatureSliderRect.x += EditorGUIUtility.labelWidth + k_ValueUnitSeparator;
temperatureSliderRect.width -= EditorGUIUtility.labelWidth + k_ValueUnitSeparator;
k_LightUnitSliderUIDrawer.DrawTemperatureSlider(serialized.settings, serialized.settings.colorTemperature, temperatureSliderRect);
// Value and unit label
// Match const defined in EditorGUI.cs
const int k_IndentPerLevel = 15;
const int k_UnitWidth = 100 + k_IndentPerLevel;
int indent = k_IndentPerLevel * EditorGUI.indentLevel;
Rect valueRect = EditorGUILayout.GetControlRect();
valueRect.width += indent - k_ValueUnitSeparator - k_UnitWidth;
Rect unitRect = valueRect;
unitRect.x += valueRect.width - indent + k_ValueUnitSeparator;
unitRect.width = k_UnitWidth + .5f;
EditorGUI.PropertyField(valueRect, serialized.settings.colorTemperature, s_Styles.empty);
EditorGUI.Popup(unitRect, 0, new[] {"Kelvin"});
EditorGUI.indentLevel -= 1;
}
else
EditorGUILayout.PropertyField(serialized.settings.color, s_Styles.color);
}
else
EditorGUILayout.PropertyField(serialized.settings.color, s_Styles.color);
}
DrawLightIntensityGUILayout(serialized, owner);
HDLightType lightType = serialized.type;
SpotLightShape spotLightShape = serialized.spotLightShape.GetEnumValue<SpotLightShape>();
LightUnit lightUnit = serialized.lightUnit.GetEnumValue<LightUnit>();
if (lightType != HDLightType.Directional
// Box are local directional light and shouldn't display the Lux At widget. It use only lux
&& !(lightType == HDLightType.Spot && (spotLightShape == SpotLightShape.Box))
&& lightUnit == (LightUnit)PunctualLightUnit.Lux)
{
EditorGUI.indentLevel++;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(serialized.luxAtDistance, s_Styles.luxAtDistance);
if (EditorGUI.EndChangeCheck())
{
serialized.luxAtDistance.floatValue = Mathf.Max(serialized.luxAtDistance.floatValue, 0.01f);
}
EditorGUI.indentLevel--;
}
if (lightType == HDLightType.Spot
&& (spotLightShape == SpotLightShape.Cone || spotLightShape == SpotLightShape.Pyramid)
// Display reflector only in advance mode
&& (lightUnit == (int)PunctualLightUnit.Lumen && GetAdvanced(AdvancedMode.Emission, serialized, owner)))
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(serialized.enableSpotReflector, s_Styles.enableSpotReflector);
EditorGUI.indentLevel--;
}
if (lightType != HDLightType.Directional)
{
EditorGUI.BeginChangeCheck();
#if UNITY_2020_1_OR_NEWER
serialized.settings.DrawRange();
#else
serialized.settings.DrawRange(false);
#endif
// Make sure the range is not 0.0
serialized.settings.range.floatValue = Mathf.Max(0.001f, serialized.settings.range.floatValue);
if (EditorGUI.EndChangeCheck())
{
// For GI we need to detect any change on additional data and call SetLightDirty + For intensity we need to detect light shape change
serialized.needUpdateAreaLightEmissiveMeshComponents = true;
SetLightsDirty(owner); // Should be apply only to parameter that's affect GI, but make the code cleaner
}
}
serialized.settings.DrawBounceIntensity();
EditorGUI.BeginChangeCheck(); // For GI we need to detect any change on additional data and call SetLightDirty
if (lightType != HDLightType.Area)
{
serialized.settings.DrawCookie();
// When directional light use a cookie, it can control the size
if (serialized.settings.cookie != null && lightType == HDLightType.Directional)
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(serialized.shapeWidth, s_Styles.cookieSizeX);
EditorGUILayout.PropertyField(serialized.shapeHeight, s_Styles.cookieSizeY);
EditorGUI.indentLevel--;
}
ShowCookieTextureWarnings(serialized.settings.cookie, serialized.settings.isCompletelyBaked || serialized.settings.isBakedOrMixed);
}
else if (serialized.areaLightShape == AreaLightShape.Rectangle || serialized.areaLightShape == AreaLightShape.Disc)
{
EditorGUILayout.ObjectField(serialized.areaLightCookie, s_Styles.areaLightCookie);
ShowCookieTextureWarnings(serialized.areaLightCookie.objectReferenceValue as Texture, serialized.settings.isCompletelyBaked || serialized.settings.isBakedOrMixed);
}
if (serialized.type == HDLightType.Point || serialized.type == HDLightType.Spot || (serialized.type == HDLightType.Area && serialized.areaLightShape == AreaLightShape.Rectangle))
{
EditorGUI.BeginChangeCheck();
UnityEngine.Object iesAsset = EditorGUILayout.ObjectField(
s_Styles.iesTexture,
serialized.type == HDLightType.Point ? serialized.iesPoint.objectReferenceValue : serialized.iesSpot.objectReferenceValue,
typeof(IESObject), false);
if (EditorGUI.EndChangeCheck())
{
SerializedProperty pointTex = serialized.iesPoint;
SerializedProperty spotTex = serialized.iesSpot;
if (iesAsset == null)
{
pointTex.objectReferenceValue = null;
spotTex.objectReferenceValue = null;
}
else
{
string guid;
long localID;
AssetDatabase.TryGetGUIDAndLocalFileIdentifier(iesAsset, out guid, out localID);
string path = AssetDatabase.GUIDToAssetPath(guid);
UnityEngine.Object[] textures = AssetDatabase.LoadAllAssetRepresentationsAtPath(path);
foreach (var subAsset in textures)
{
if (AssetDatabase.IsSubAsset(subAsset) && subAsset.name.EndsWith("-Cube-IES"))
{
pointTex.objectReferenceValue = subAsset;
}
else if (AssetDatabase.IsSubAsset(subAsset) && subAsset.name.EndsWith("-2D-IES"))
{
spotTex.objectReferenceValue = subAsset;
}
}
}
serialized.iesPoint.serializedObject.ApplyModifiedProperties();
serialized.iesSpot.serializedObject.ApplyModifiedProperties();
}
}
if (serialized.type == HDLightType.Spot &&
serialized.spotLightShape.enumValueIndex == (int)SpotLightShape.Cone &&
serialized.iesSpot.objectReferenceValue != null)
{
EditorGUILayout.PropertyField(serialized.spotIESCutoffPercent, s_Styles.spotIESCutoffPercent);
}
if (EditorGUI.EndChangeCheck())
{
serialized.needUpdateAreaLightEmissiveMeshComponents = true;
SetLightsDirty(owner); // Should be apply only to parameter that's affect GI, but make the code cleaner
}
}
static void ShowCookieTextureWarnings(Texture cookie, bool useBaking)
{
if (cookie == null)
return;
// The texture type is stored in the texture importer so we need to get it:
TextureImporter texImporter = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(cookie)) as TextureImporter;
if (texImporter != null && texImporter.textureType == TextureImporterType.Cookie)
{
using (new EditorGUILayout.HorizontalScope())
{
int indentSpace = (int)EditorGUI.IndentedRect(new Rect()).x;
GUILayout.Space(indentSpace);
using (new EditorGUILayout.HorizontalScope(EditorStyles.helpBox))
{
int oldIndentLevel = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
GUIStyle wordWrap = new GUIStyle(EditorStyles.miniLabel){ wordWrap = true};
EditorGUILayout.LabelField(s_Styles.cookieTextureTypeError, wordWrap);
if (GUILayout.Button("Fix", GUILayout.ExpandHeight(true)))
{
texImporter.textureType = TextureImporterType.Default;
texImporter.SaveAndReimport();
}
EditorGUI.indentLevel = oldIndentLevel;
}
}
}
if (useBaking && !UnityEditor.EditorSettings.enableCookiesInLightmapper)
EditorGUILayout.HelpBox(s_Styles.cookieBaking, MessageType.Warning);
if (cookie.width != cookie.height)
EditorGUILayout.HelpBox(s_Styles.cookieNonPOT, MessageType.Warning);
if (cookie.width < LightCookieManager.k_MinCookieSize || cookie.height < LightCookieManager.k_MinCookieSize)
EditorGUILayout.HelpBox(s_Styles.cookieTooSmall, MessageType.Warning);
}
static void DrawEmissionAdvancedContent(SerializedHDLight serialized, Editor owner)
{
HDLightType lightType = serialized.type;
EditorGUI.BeginChangeCheck(); // For GI we need to detect any change on additional data and call SetLightDirty
bool bakedOnly = serialized.settings.isCompletelyBaked;
if (!bakedOnly)
{
EditorGUILayout.PropertyField(serialized.affectDiffuse, s_Styles.affectDiffuse);
EditorGUILayout.PropertyField(serialized.affectSpecular, s_Styles.affectSpecular);
if (lightType != HDLightType.Directional)
{
EditorGUILayout.PropertyField(serialized.applyRangeAttenuation, s_Styles.applyRangeAttenuation);
EditorGUILayout.PropertyField(serialized.fadeDistance, s_Styles.fadeDistance);
}
EditorGUILayout.PropertyField(serialized.lightDimmer, s_Styles.lightDimmer);
}
else if (lightType == HDLightType.Point || lightType == HDLightType.Spot)
EditorGUILayout.PropertyField(serialized.applyRangeAttenuation, s_Styles.applyRangeAttenuation);
// Emissive mesh for area light only (and not supported on Disc currently)
if (lightType == HDLightType.Area && serialized.areaLightShape != AreaLightShape.Disc)
{
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(serialized.displayAreaLightEmissiveMesh, s_Styles.displayAreaLightEmissiveMesh);
if (EditorGUI.EndChangeCheck())
{
serialized.FetchAreaLightEmissiveMeshComponents();
serialized.needUpdateAreaLightEmissiveMeshComponents = true;
}
bool showSubArea = serialized.displayAreaLightEmissiveMesh.boolValue && !serialized.displayAreaLightEmissiveMesh.hasMultipleDifferentValues;
++EditorGUI.indentLevel;
Rect lineRect = EditorGUILayout.GetControlRect();
ShadowCastingMode newCastShadow;
EditorGUI.showMixedValue = serialized.areaLightEmissiveMeshCastShadow.hasMultipleDifferentValues;
EditorGUI.BeginChangeCheck();
using (new SerializedHDLight.AreaLightEmissiveMeshDrawScope(lineRect, s_Styles.areaLightEmissiveMeshCastShadow, showSubArea, serialized.areaLightEmissiveMeshCastShadow, serialized.deportedAreaLightEmissiveMeshCastShadow))
{
newCastShadow = (ShadowCastingMode)EditorGUI.EnumPopup(lineRect, s_Styles.areaLightEmissiveMeshCastShadow, (ShadowCastingMode)serialized.areaLightEmissiveMeshCastShadow.intValue);
}
if (EditorGUI.EndChangeCheck())
{
serialized.UpdateAreaLightEmissiveMeshCastShadow(newCastShadow);
}
EditorGUI.showMixedValue = false;
lineRect = EditorGUILayout.GetControlRect();
SerializedHDLight.MotionVector newMotionVector;
EditorGUI.showMixedValue = serialized.areaLightEmissiveMeshMotionVector.hasMultipleDifferentValues;
EditorGUI.BeginChangeCheck();
using (new SerializedHDLight.AreaLightEmissiveMeshDrawScope(lineRect, s_Styles.areaLightEmissiveMeshMotionVector, showSubArea, serialized.areaLightEmissiveMeshMotionVector, serialized.deportedAreaLightEmissiveMeshMotionVector))
{
newMotionVector = (SerializedHDLight.MotionVector)EditorGUI.EnumPopup(lineRect, s_Styles.areaLightEmissiveMeshMotionVector, (SerializedHDLight.MotionVector)serialized.areaLightEmissiveMeshMotionVector.intValue);
}
if (EditorGUI.EndChangeCheck())
{
serialized.UpdateAreaLightEmissiveMeshMotionVectorGeneration(newMotionVector);
}
EditorGUI.showMixedValue = false;
EditorGUI.showMixedValue = serialized.areaLightEmissiveMeshLayer.hasMultipleDifferentValues || serialized.lightLayer.hasMultipleDifferentValues;
EditorGUI.BeginChangeCheck();
bool toggle;
using (new SerializedHDLight.AreaLightEmissiveMeshDrawScope(lineRect, s_Styles.areaLightEmissiveMeshSameLayer, showSubArea, serialized.areaLightEmissiveMeshLayer, serialized.deportedAreaLightEmissiveMeshLayer))
{
toggle = EditorGUILayout.Toggle(s_Styles.areaLightEmissiveMeshSameLayer, serialized.areaLightEmissiveMeshLayer.intValue == -1);
}
if (EditorGUI.EndChangeCheck())
{
serialized.UpdateAreaLightEmissiveMeshLayer(serialized.lightLayer.intValue);
if (toggle)
serialized.areaLightEmissiveMeshLayer.intValue = -1;
}
EditorGUI.showMixedValue = false;
++EditorGUI.indentLevel;
if (toggle || serialized.areaLightEmissiveMeshLayer.hasMultipleDifferentValues)
{
using (new EditorGUI.DisabledScope(true))
{
lineRect = EditorGUILayout.GetControlRect();
EditorGUI.showMixedValue = serialized.areaLightEmissiveMeshLayer.hasMultipleDifferentValues || serialized.lightLayer.hasMultipleDifferentValues;
EditorGUI.LayerField(lineRect, s_Styles.areaLightEmissiveMeshCustomLayer, serialized.lightLayer.intValue);
EditorGUI.showMixedValue = false;
}
}
else
{
EditorGUI.showMixedValue = serialized.areaLightEmissiveMeshLayer.hasMultipleDifferentValues;
lineRect = EditorGUILayout.GetControlRect();
int layer;
EditorGUI.BeginChangeCheck();
using (new SerializedHDLight.AreaLightEmissiveMeshDrawScope(lineRect, s_Styles.areaLightEmissiveMeshCustomLayer, showSubArea, serialized.areaLightEmissiveMeshLayer, serialized.deportedAreaLightEmissiveMeshLayer))
{
layer = EditorGUI.LayerField(lineRect, s_Styles.areaLightEmissiveMeshCustomLayer, serialized.areaLightEmissiveMeshLayer.intValue);
}
if (EditorGUI.EndChangeCheck())
{
serialized.UpdateAreaLightEmissiveMeshLayer(layer);
}
// or if the value of layer got changed using the layer change including child mechanism (strangely apply even if object not editable),
// discard the change: the child is not saved anyway so the value in HDAdditionalLightData is the only serialized one.
else if (!EditorGUI.showMixedValue
&& serialized.deportedAreaLightEmissiveMeshLayer != null
&& !serialized.deportedAreaLightEmissiveMeshLayer.Equals(null)
&& serialized.areaLightEmissiveMeshLayer.intValue != serialized.deportedAreaLightEmissiveMeshLayer.intValue)
{
GUI.changed = true; //force register change to handle update and apply later
serialized.UpdateAreaLightEmissiveMeshLayer(layer);
}
EditorGUI.showMixedValue = false;
}
--EditorGUI.indentLevel;
--EditorGUI.indentLevel;
}
EditorGUILayout.PropertyField(serialized.includeForRayTracing, s_Styles.includeLightForRayTracing);
if (EditorGUI.EndChangeCheck())
{
serialized.needUpdateAreaLightEmissiveMeshComponents = true;
serialized.fadeDistance.floatValue = Mathf.Max(serialized.fadeDistance.floatValue, 0.01f);
SetLightsDirty(owner); // Should be apply only to parameter that's affect GI, but make the code cleaner
}
}
static void DrawVolumetric(SerializedHDLight serialized, Editor owner)
{
EditorGUILayout.PropertyField(serialized.useVolumetric, s_Styles.volumetricEnable);
using (new EditorGUI.DisabledScope(!serialized.useVolumetric.boolValue))
{
EditorGUILayout.PropertyField(serialized.volumetricDimmer, s_Styles.volumetricDimmer);
EditorGUILayout.Slider(serialized.volumetricShadowDimmer, 0.0f, 1.0f, s_Styles.volumetricShadowDimmer);
HDLightType lightType = serialized.type;
if (lightType != HDLightType.Directional)
{
EditorGUILayout.PropertyField(serialized.volumetricFadeDistance, s_Styles.volumetricFadeDistance);
}
}
}
static bool DrawEnableShadowMap(SerializedHDLight serialized, Editor owne)
{
Rect lineRect = EditorGUILayout.GetControlRect();
bool newShadowsEnabled;
EditorGUI.BeginProperty(lineRect, s_Styles.enableShadowMap, serialized.settings.shadowsType);
{
bool oldShadowEnabled = serialized.settings.shadowsType.GetEnumValue<LightShadows>() != LightShadows.None;
newShadowsEnabled = EditorGUI.Toggle(lineRect, s_Styles.enableShadowMap, oldShadowEnabled);
if (oldShadowEnabled ^ newShadowsEnabled)
{
serialized.settings.shadowsType.SetEnumValue(newShadowsEnabled ? LightShadows.Hard : LightShadows.None);
}
}
EditorGUI.EndProperty();
return newShadowsEnabled;
}
static void DrawShadowMapContent(SerializedHDLight serialized, Editor owner)
{
var hdrp = HDRenderPipeline.currentAsset;
bool newShadowsEnabled = DrawEnableShadowMap(serialized, owner);
using (new EditorGUI.DisabledScope(!newShadowsEnabled))
{
EditorGUILayout.PropertyField(serialized.shadowUpdateMode, s_Styles.shadowUpdateMode);
if (serialized.shadowUpdateMode.intValue > 0 && serialized.type != HDLightType.Directional)
{
#if UNITY_2021_1_OR_NEWER
EditorGUILayout.PropertyField(serialized.shadowAlwaysDrawDynamic, s_Styles.shadowAlwaysDrawDynamic);
#endif
}
EditorGUILayout.PropertyField(serialized.shadowUpdateUponTransformChange, s_Styles.shadowUpdateOnLightTransformChange);
HDLightType lightType = serialized.type;
using (var change = new EditorGUI.ChangeCheckScope())
{
var hasEditorLightShapeMultipleValues = lightType == (HDLightType)(-1);
if (hasEditorLightShapeMultipleValues)
{
serialized.shadowResolution.LevelAndIntGUILayout(
s_Styles.shadowResolution, null, null
);
}
else
{
var scalableSetting = ScalableSettings.ShadowResolution(lightType, hdrp);
serialized.shadowResolution.LevelAndIntGUILayout(
s_Styles.shadowResolution, scalableSetting, hdrp.name
);
}
if (change.changed)
serialized.shadowResolution.@override.intValue = Mathf.Max(HDShadowManager.k_MinShadowMapResolution, serialized.shadowResolution.@override.intValue);
}
if (lightType != HDLightType.Directional)
EditorGUILayout.Slider(serialized.shadowNearPlane, HDShadowUtils.k_MinShadowNearPlane, HDShadowUtils.k_MaxShadowNearPlane, s_Styles.shadowNearPlane);
if (serialized.settings.isMixed)
{
using (new EditorGUI.DisabledScope(!HDRenderPipeline.currentAsset.currentPlatformRenderPipelineSettings.supportShadowMask))
{
Rect nonLightmappedOnlyRect = EditorGUILayout.GetControlRect();
EditorGUI.BeginProperty(nonLightmappedOnlyRect, s_Styles.nonLightmappedOnly, serialized.nonLightmappedOnly);
{
EditorGUI.BeginChangeCheck();
ShadowmaskMode shadowmask = serialized.nonLightmappedOnly.boolValue ? ShadowmaskMode.Shadowmask : ShadowmaskMode.DistanceShadowmask;
shadowmask = (ShadowmaskMode)EditorGUI.EnumPopup(nonLightmappedOnlyRect, s_Styles.nonLightmappedOnly, shadowmask);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObjects(owner.targets, "Light Update Shadowmask Mode");
serialized.nonLightmappedOnly.boolValue = shadowmask == ShadowmaskMode.Shadowmask;
foreach (Light target in owner.targets)
target.lightShadowCasterMode = shadowmask == ShadowmaskMode.Shadowmask ? LightShadowCasterMode.NonLightmappedOnly : LightShadowCasterMode.Everything;
}
}
EditorGUI.EndProperty();
}
}
if (lightType == HDLightType.Area && serialized.areaLightShape == AreaLightShape.Rectangle)
{
EditorGUILayout.Slider(serialized.areaLightShadowCone, HDAdditionalLightData.k_MinAreaLightShadowCone, HDAdditionalLightData.k_MaxAreaLightShadowCone, s_Styles.areaLightShadowCone);
}
if (HDRenderPipeline.pipelineSupportsRayTracing && HDRenderPipeline.pipelineSupportsScreenSpaceShadows)
{
bool isPunctual = lightType == HDLightType.Point || (lightType == HDLightType.Spot && serialized.spotLightShape.GetEnumValue<SpotLightShape>() == SpotLightShape.Cone);
if (isPunctual || (lightType == HDLightType.Area && serialized.areaLightShape == AreaLightShape.Rectangle))
{
EditorGUILayout.PropertyField(serialized.useRayTracedShadows, s_Styles.useRayTracedShadows);
if (serialized.useRayTracedShadows.boolValue)
{
if (hdrp != null && lightType == HDLightType.Area && serialized.areaLightShape == AreaLightShape.Rectangle
&& (hdrp.currentPlatformRenderPipelineSettings.supportedLitShaderMode != RenderPipelineSettings.SupportedLitShaderMode.DeferredOnly))
EditorGUILayout.HelpBox("Ray traced area light shadows are approximated for the Lit shader when not in deferred mode.", MessageType.Warning);
EditorGUI.indentLevel++;
// We only support semi transparent shadows for punctual lights
if (isPunctual)
EditorGUILayout.PropertyField(serialized.semiTransparentShadow, s_Styles.semiTransparentShadow);
EditorGUILayout.PropertyField(serialized.numRayTracingSamples, s_Styles.numRayTracingSamples);
EditorGUILayout.PropertyField(serialized.filterTracedShadow, s_Styles.denoiseTracedShadow);
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(serialized.filterSizeTraced, s_Styles.denoiserRadius);
// We only support distance based filtering if we have a punctual light source (point or spot)
if (isPunctual)
EditorGUILayout.PropertyField(serialized.distanceBasedFiltering, s_Styles.distanceBasedFiltering);
EditorGUI.indentLevel--;
EditorGUI.indentLevel--;
}
}
}
// For the moment, we only support screen space rasterized shadows for directional lights
if (lightType == HDLightType.Directional && HDRenderPipeline.pipelineSupportsScreenSpaceShadows)
{
EditorGUILayout.PropertyField(serialized.useScreenSpaceShadows, s_Styles.useScreenSpaceShadows);
if (HDRenderPipeline.pipelineSupportsRayTracing)
{
using (new EditorGUI.DisabledScope(!serialized.useScreenSpaceShadows.boolValue))
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(serialized.useRayTracedShadows, s_Styles.useRayTracedShadows);
using (new EditorGUI.DisabledScope(!serialized.useRayTracedShadows.boolValue))
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(serialized.numRayTracingSamples, s_Styles.numRayTracingSamples);
EditorGUILayout.PropertyField(serialized.colorShadow, s_Styles.colorShadow);
EditorGUILayout.PropertyField(serialized.filterTracedShadow, s_Styles.denoiseTracedShadow);
using (new EditorGUI.DisabledScope(!serialized.filterTracedShadow.boolValue))
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(serialized.filterSizeTraced, s_Styles.denoiserRadius);
EditorGUI.indentLevel--;
}
EditorGUI.indentLevel--;
}
EditorGUI.indentLevel--;
}
}
}
}
}
static void DrawShadowMapAdvancedContent(SerializedHDLight serialized, Editor owner)
{
using (new EditorGUI.DisabledScope(serialized.settings.shadowsType.GetEnumValue<LightShadows>() == LightShadows.None))
{
HDLightType lightType = serialized.type;
if (lightType == HDLightType.Area && serialized.areaLightShape == AreaLightShape.Rectangle)
{
EditorGUILayout.Slider(serialized.evsmExponent, HDAdditionalLightData.k_MinEvsmExponent, HDAdditionalLightData.k_MaxEvsmExponent, s_Styles.evsmExponent);
EditorGUILayout.Slider(serialized.evsmLightLeakBias, HDAdditionalLightData.k_MinEvsmLightLeakBias, HDAdditionalLightData.k_MaxEvsmLightLeakBias, s_Styles.evsmLightLeakBias);
EditorGUILayout.Slider(serialized.evsmVarianceBias, HDAdditionalLightData.k_MinEvsmVarianceBias, HDAdditionalLightData.k_MaxEvsmVarianceBias, s_Styles.evsmVarianceBias);
EditorGUILayout.IntSlider(serialized.evsmBlurPasses, HDAdditionalLightData.k_MinEvsmBlurPasses, HDAdditionalLightData.k_MaxEvsmBlurPasses, s_Styles.evsmAdditionalBlurPasses);
}
else
{
EditorGUILayout.Slider(serialized.slopeBias, 0.0f, 1.0f, s_Styles.slopeBias);
EditorGUILayout.Slider(serialized.normalBias, 0.0f, 5.0f, s_Styles.normalBias);
if (lightType == HDLightType.Spot
&& serialized.spotLightShape.GetEnumValue<SpotLightShape>() != SpotLightShape.Box)
{
EditorGUILayout.PropertyField(serialized.useCustomSpotLightShadowCone, s_Styles.useCustomSpotLightShadowCone);
if (serialized.useCustomSpotLightShadowCone.boolValue)
{
EditorGUILayout.Slider(serialized.customSpotLightShadowCone, 1.0f, serialized.settings.spotAngle.floatValue, s_Styles.customSpotLightShadowCone);
}
}
}
// Dimmer and Tint don't have effect on baked shadow
if (!serialized.settings.isCompletelyBaked)
{
EditorGUILayout.Slider(serialized.shadowDimmer, 0.0f, 1.0f, s_Styles.shadowDimmer);
EditorGUILayout.PropertyField(serialized.shadowTint, s_Styles.shadowTint);
EditorGUILayout.PropertyField(serialized.penumbraTint, s_Styles.penumbraTint);
}
if (lightType != HDLightType.Directional)
{
EditorGUILayout.PropertyField(serialized.shadowFadeDistance, s_Styles.shadowFadeDistance);
}
// Shadow Layers
using (new EditorGUI.DisabledScope(!HDUtils.hdrpSettings.supportLightLayers))
{
using (var change = new EditorGUI.ChangeCheckScope())
{
EditorGUILayout.PropertyField(serialized.linkLightLayers, s_Styles.linkLightAndShadowLayersText);
// Undo the changes in the light component because the SyncLightAndShadowLayers will change the value automatically when link is ticked
if (change.changed)
Undo.RecordObjects(owner.targets, "Undo Light Layers Changed");
}
if (!serialized.linkLightLayers.hasMultipleDifferentValues && !serialized.lightlayersMask.hasMultipleDifferentValues)
{
using (new EditorGUI.DisabledGroupScope(serialized.linkLightLayers.boolValue))
{
HDEditorUtils.DrawLightLayerMaskFromInt(s_Styles.shadowLayerMaskText, serialized.settings.renderingLayerMask);
}
if (serialized.linkLightLayers.boolValue)
SyncLightAndShadowLayers(serialized, owner);
}
}
}
}
static void SyncLightAndShadowLayers(SerializedHDLight serialized, Editor owner)
{
// If we're not in decoupled mode for light layers, we sync light with shadow layers:
foreach (Light target in owner.targets)
if (target.renderingLayerMask != serialized.lightlayersMask.intValue)
target.renderingLayerMask = serialized.lightlayersMask.intValue;
}
static void DrawContactShadowsContent(SerializedHDLight serialized, Editor owner)
{
var hdrp = HDRenderPipeline.currentAsset;
SerializedScalableSettingValueUI.LevelAndToggleGUILayout(
serialized.contactShadows,
s_Styles.contactShadows,
HDAdditionalLightData.ScalableSettings.UseContactShadow(hdrp),
hdrp.name
);
if (HDRenderPipeline.pipelineSupportsRayTracing
&& serialized.contactShadows.@override.boolValue)
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(serialized.rayTracedContactShadow, s_Styles.rayTracedContactShadow);
EditorGUI.indentLevel--;
}
}
static void DrawBakedShadowsContent(SerializedHDLight serialized, Editor owner)
{
DrawEnableShadowMap(serialized, owner);
if (serialized.type != HDLightType.Directional)
EditorGUILayout.Slider(serialized.shadowNearPlane, HDShadowUtils.k_MinShadowNearPlane, HDShadowUtils.k_MaxShadowNearPlane, s_Styles.shadowNearPlane);
}
static bool HasShadowQualitySettingsUI(HDShadowFilteringQuality quality, SerializedHDLight serialized, Editor owner)
{
// Handle quality where there is nothing to draw directly here
// No PCSS for now with directional light
if (quality == HDShadowFilteringQuality.Medium || quality == HDShadowFilteringQuality.Low)
return false;
// Draw shadow settings using the current shadow algorithm
HDShadowInitParameters hdShadowInitParameters = HDRenderPipeline.currentAsset.currentPlatformRenderPipelineSettings.hdShadowInitParams;
return hdShadowInitParameters.shadowFilteringQuality == quality;
}
static void DrawLowShadowSettingsContent(SerializedHDLight serialized, Editor owner)
{
// Currently there is nothing to display here
// when adding something, update IsShadowSettings
}
static void DrawMediumShadowSettingsContent(SerializedHDLight serialized, Editor owner)
{
// Currently there is nothing to display here
// when adding something, update IsShadowSettings
}
static void DrawHighShadowSettingsContent(SerializedHDLight serialized, Editor owner)
{
EditorGUILayout.PropertyField(serialized.blockerSampleCount, s_Styles.blockerSampleCount);
EditorGUILayout.PropertyField(serialized.filterSampleCount, s_Styles.filterSampleCount);
EditorGUILayout.PropertyField(serialized.minFilterSize, s_Styles.minFilterSize);
GUIContent styleForScale = s_Styles.radiusScaleForSoftness;
if (serialized.type == HDLightType.Directional)
{
styleForScale = s_Styles.diameterScaleForSoftness;
}
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(serialized.scaleForSoftness, styleForScale);
if (EditorGUI.EndChangeCheck())
{
//Clamp the value and also affect baked shadows
serialized.scaleForSoftness.floatValue = Mathf.Max(serialized.scaleForSoftness.floatValue, 0);
}
}
static void DrawVeryHighShadowSettingsContent(SerializedHDLight serialized, Editor owner)
{
EditorGUILayout.PropertyField(serialized.kernelSize, s_Styles.kernelSize);
EditorGUILayout.PropertyField(serialized.lightAngle, s_Styles.lightAngle);
EditorGUILayout.PropertyField(serialized.maxDepthBias, s_Styles.maxDepthBias);
}
static void SetLightsDirty(Editor owner)
{
foreach (Light light in owner.targets)
light.SetLightDirty(); // Should be apply only to parameter that's affect GI, but make the code cleaner
}
}
}