using System ;
using System.Linq ;
using UnityEngine.Rendering ;
// TODO: Simplify this editor once we can target 2018.1
sealed class SerializedBaseData
{
public SerializedProperty type ;
public SerializedProperty range ;
public SerializedProperty spotAngle ;
public SerializedProperty cookie ;
public SerializedProperty cookieSize ;
public SerializedProperty color ;
public SerializedProperty intensity ;
public SerializedProperty bounceIntensity ;
public SerializedProperty colorTemperature ;
public SerializedProperty useColorTemperature ;
public SerializedProperty shadowsType ;
public SerializedProperty shadowsBias ;
public SerializedProperty shadowsNormalBias ;
public SerializedProperty shadowsNearPlane ;
public SerializedProperty lightmapping ;
public SerializedProperty areaSizeX ;
public SerializedProperty areaSizeY ;
public SerializedProperty bakedShadowRadius ;
public SerializedProperty bakedShadowAngle ;
}
sealed class SerializedLightData
{
public SerializedProperty spotInnerPercent ;
SerializedObject m_SerializedAdditionalLightData ;
SerializedObject m_SerializedAdditionalShadowData ;
SerializedBaseData m_BaseData ;
// Copied over from teh original LightEditor class. Will go away once we can target 2018.1
bool m_TypeIsSame { get { return ! m_BaseData . type . hasMultipleDifferentValues ; } }
bool m_LightmappingTypeIsSame { get { return ! m_BaseData . lightmapping . hasMultipleDifferentValues ; } }
bool m_IsCompletelyBaked { get { return m_BaseData . lightmapping . intValue = = 2 ; } }
bool m_IsRealtime { get { return m_BaseData . lightmapping . intValue = = 4 ; } }
Light light { get { return serializedObject . targetObject as Light ; } }
Texture m_Cookie { get { return m_BaseData . cookie . objectReferenceValue as Texture ; } }
bool m_BakingWarningValue { get { return ! UnityEditor . Lightmapping . bakedGI & & m_LightmappingTypeIsSame & & ! m_IsRealtime ; } }
bool m_BounceWarningValue
{
get
{
return m_TypeIsSame & & ( light . type = = LightType . Point | | light . type = = LightType . Spot ) & &
m_LightmappingTypeIsSame & & m_IsRealtime & & ! m_BaseData . bounceIntensity . hasMultipleDifferentValues
& & m_BaseData . bounceIntensity . floatValue > 0.0f ;
}
}
public bool cookieWarningValue
{
get
{
return m_TypeIsSame & & light . type = = LightType . Spot & &
! m_BaseData . cookie . hasMultipleDifferentValues & & m_Cookie & & m_Cookie . wrapMode ! = TextureWrapMode . Clamp ;
}
}
// LightType + LightTypeExtent combined
enum LightShape
{
// Used for UI only; the processing code must use LightTypeExtent and LightType
LightShape m_LightShape ;
void OnEnable ( )
protected override void OnEnable ( )
base . OnEnable ( ) ;
var lightData = GetAdditionalData < HDAdditionalLightData > ( ) ;
var shadowData = GetAdditionalData < AdditionalShadowData > ( ) ;
var lightData = CoreEditorUtils . GetAdditionalData < HDAdditionalLightData > ( targets ) ;
var shadowData = CoreEditorUtils . GetAdditionalData < AdditionalShadowData > ( targets ) ;
// Grab all the serialized data we need
m_BaseData = new SerializedBaseData
{
type = serializedObject . FindProperty ( "m_Type" ) ,
range = serializedObject . FindProperty ( "m_Range" ) ,
spotAngle = serializedObject . FindProperty ( "m_SpotAngle" ) ,
cookie = serializedObject . FindProperty ( "m_Cookie" ) ,
cookieSize = serializedObject . FindProperty ( "m_CookieSize" ) ,
color = serializedObject . FindProperty ( "m_Color" ) ,
intensity = serializedObject . FindProperty ( "m_Intensity" ) ,
bounceIntensity = serializedObject . FindProperty ( "m_BounceIntensity" ) ,
colorTemperature = serializedObject . FindProperty ( "m_ColorTemperature" ) ,
useColorTemperature = serializedObject . FindProperty ( "m_UseColorTemperature" ) ,
shadowsType = serializedObject . FindProperty ( "m_Shadows.m_Type" ) ,
shadowsBias = serializedObject . FindProperty ( "m_Shadows.m_Bias" ) ,
shadowsNormalBias = serializedObject . FindProperty ( "m_Shadows.m_NormalBias" ) ,
shadowsNearPlane = serializedObject . FindProperty ( "m_Shadows.m_NearPlane" ) ,
lightmapping = serializedObject . FindProperty ( "m_Lightmapping" ) ,
areaSizeX = serializedObject . FindProperty ( "m_AreaSize.x" ) ,
areaSizeY = serializedObject . FindProperty ( "m_AreaSize.y" ) ,
bakedShadowRadius = serializedObject . FindProperty ( "m_ShadowRadius" ) ,
bakedShadowAngle = serializedObject . FindProperty ( "m_ShadowAngle" )
} ;
using ( var o = new PropertyFetcher < HDAdditionalLightData > ( m_SerializedAdditionalLightData ) )
m_AdditionalLightData = new SerializedLightData
{
ApplyAdditionalComponentsVisibility ( true ) ;
CheckStyles ( ) ;
serializedObject . Update ( ) ;
settings . Update ( ) ;
DrawFoldout ( m_BaseData . t ype, "Shape" , DrawShape ) ;
DrawFoldout ( m_BaseData . intensity , "Light" , DrawLightSettings ) ;
DrawFoldout ( settings . lightT ype, "Shape" , DrawShape ) ;
DrawFoldout ( settings . intensity , "Light" , DrawLightSettings ) ;
if ( m_BaseData . shadowsType . enumValueIndex ! = ( int ) LightShadows . None )
DrawFoldout ( m_BaseData . shadowsType , "Shadows" , DrawShadows ) ;
if ( settings . shadowsType . enumValueIndex ! = ( int ) LightShadows . None )
DrawFoldout ( settings . shadowsType , "Shadows" , DrawShadows ) ;
CoreEditorUtils . DrawSplitter ( ) ;
EditorGUILayout . Space ( ) ;
serializedObject . ApplyModifiedProperties ( ) ;
settings . ApplyModifiedProperties ( ) ;
}
void DrawFoldout ( SerializedProperty foldoutProperty , string title , Action func )
void DrawFeatures ( )
{
EditorGUILayout . PropertyField ( m_AdditionalLightData . showAdditionalSettings ) ;
bool disabledScope = m_IsCompletelyBaked
bool disabledScope = settings . isCompletelyBaked
bool shadowsEnabled = EditorGUILayout . Toggle ( new GUIContent ( "Enable Shadows" ) , m_BaseData . shadowsType . enumValueIndex ! = 0 ) ;
m_BaseData . shadowsType . enumValueIndex = shadowsEnabled ? ( int ) LightShadows . Hard : ( int ) LightShadows . None ;
bool shadowsEnabled = EditorGUILayout . Toggle ( CoreEditorUtils . GetContent ( "Enable Shadows" ) , settings . shadowsType . enumValueIndex ! = 0 ) ;
settings . shadowsType . enumValueIndex = shadowsEnabled ? ( int ) LightShadows . Hard : ( int ) LightShadows . None ;
EditorGUILayout . PropertyField ( m_AdditionalLightData . showAdditionalSettings ) ;
}
void DrawShape ( )
switch ( m_LightShape )
{
case LightShape . Directional :
m_BaseData . t ype. enumValueIndex = ( int ) LightType . Directional ;
settings . lightT ype. enumValueIndex = ( int ) LightType . Directional ;
m_BaseData . t ype. enumValueIndex = ( int ) LightType . Point ;
settings . lightT ype. enumValueIndex = ( int ) LightType . Point ;
m_BaseData . t ype. enumValueIndex = ( int ) LightType . Spot ;
settings . lightT ype. enumValueIndex = ( int ) LightType . Spot ;
m_AdditionalLightData . lightTypeExtent . enumValueIndex = ( int ) LightTypeExtent . Punctual ;
EditorGUILayout . PropertyField ( m_AdditionalLightData . spotLightShape , s_Styles . spotLightShape ) ;
var spotLightShape = ( SpotLightShape ) m_AdditionalLightData . spotLightShape . enumValueIndex ;
EditorGUILayout . Slider ( m_BaseData . spotAngle , 0f , 1 7 9.9f , s_Styles . spotAngle ) ;
settings . DrawSpotAngle ( ) ;
EditorGUILayout . Slider ( m_BaseData . spotAngle , 0f , 1 7 9.9f , s_Styles . spotAngle ) ;
settings . DrawSpotAngle ( ) ;
EditorGUILayout . Slider ( m_AdditionalLightData . aspectRatio , 0.05f , 2 0.0f , s_Styles . aspectRatioPyramid ) ;
}
else if ( spotLightShape = = SpotLightShape . Box )
case LightShape . Rectangle :
// TODO: Currently if we use Area type as it is offline light in legacy, the light will not exist at runtime
//m_BaseData.type.enumValueIndex = (int)LightType.Area;
m_BaseData . t ype. enumValueIndex = ( int ) LightType . Point ;
settings . lightT ype. enumValueIndex = ( int ) LightType . Point ;
m_BaseData . areaSizeX . floatValue = m_AdditionalLightData . shapeLength . floatValue ;
m_BaseData . areaSizeY . floatValue = m_AdditionalLightData . shapeWidth . floatValue ;
m_BaseData . shadowsType . enumValueIndex = ( int ) LightShadows . None ;
settings . areaSizeX . floatValue = m_AdditionalLightData . shapeLength . floatValue ;
settings . areaSizeY . floatValue = m_AdditionalLightData . shapeWidth . floatValue ;
settings . shadowsType . enumValueIndex = ( int ) LightShadows . None ;
m_BaseData . t ype. enumValueIndex = ( int ) LightType . Point ;
settings . lightT ype. enumValueIndex = ( int ) LightType . Point ;
m_BaseData . areaSizeX . floatValue = m_AdditionalLightData . shapeLength . floatValue ;
m_BaseData . areaSizeY . floatValue = 0.01f ;
m_BaseData . shadowsType . enumValueIndex = ( int ) LightShadows . None ;
settings . areaSizeX . floatValue = m_AdditionalLightData . shapeLength . floatValue ;
settings . areaSizeY . floatValue = 0.01f ;
settings . shadowsType . enumValueIndex = ( int ) LightShadows . None ;
break ;
case ( LightShape ) ( - 1 ) :
void DrawLightSettings ( )
{
if ( GraphicsSettings . lightsUseLinearIntensity & & GraphicsSettings . lightsUseColorTemperature )
{
EditorGUILayout . PropertyField ( m_BaseData . useColorTemperature , s_Styles . useColorTemperature ) ;
if ( m_BaseData . useColorTemperature . boolValue )
{
const float kMinKelvin = 1 0 0 0f ;
const float kMaxKelvin = 2 0 0 0 0f ;
EditorGUILayout . LabelField ( s_Styles . color ) ;
EditorGUI . indentLevel + = 1 ;
EditorGUILayout . PropertyField ( m_BaseData . color , s_Styles . colorFilter ) ;
EditorGUILayout . Slider ( m_BaseData . colorTemperature , kMinKelvin , kMaxKelvin , s_Styles . colorTemperature ) ;
EditorGUI . indentLevel - = 1 ;
}
else EditorGUILayout . PropertyField ( m_BaseData . color , s_Styles . color ) ;
}
else EditorGUILayout . PropertyField ( m_BaseData . color , s_Styles . color ) ;
EditorGUILayout . PropertyField ( m_BaseData . intensity , s_Styles . intensity ) ;
EditorGUILayout . PropertyField ( m_BaseData . bounceIntensity , s_Styles . lightBounceIntensity ) ;
// Indirect shadows warning (Should be removed when we support realtime indirect shadows)
if ( m_BounceWarningValue )
EditorGUILayout . HelpBox ( s_Styles . indirectBounceShadowWarning . text , MessageType . Info ) ;
EditorGUILayout . PropertyField ( m_BaseData . range , s_Styles . range ) ;
EditorGUILayout . PropertyField ( m_BaseData . lightmapping , s_Styles . lightmappingMode ) ;
// Warning if GI Baking disabled and m_Lightmapping isn't realtime
if ( m_BakingWarningValue )
EditorGUILayout . HelpBox ( s_Styles . bakingWarning . text , MessageType . Info ) ;
settings . DrawColor ( ) ;
settings . DrawIntensity ( ) ;
settings . DrawBounceIntensity ( ) ;
settings . DrawRange ( false ) ;
settings . DrawLightmapping ( ) ;
EditorGUILayout . PropertyField ( m_BaseData . cookie , s_Styles . cookie ) ;
// Warn on spotlights if the cookie is set to repeat
if ( cookieWarningValue )
EditorGUILayout . HelpBox ( s_Styles . cookieWarning . text , MessageType . Warning ) ;
settings . DrawCookie ( ) ;
if ( m_Cookie ! = null & & m_LightShape = = LightShape . Directional )
if ( settings . cookie ! = null & & m_LightShape = = LightShape . Directional )
EditorGUI . indentLevel + + ;
EditorGUI . indentLevel - - ;
EditorGUILayout . Space ( ) ;
EditorGUILayout . LabelField ( "Additional Settings" , EditorStyles . boldLabel ) ;
EditorGUI . indentLevel + + ;
EditorGUILayout . PropertyField ( m_AdditionalLightData . affectDiffuse , s_Styles . affectDiffuse ) ;
}
}
void DrawShadows ( )
void DrawBakedShadowParameters ( )
if ( m_IsCompletelyBaked )
switch ( ( LightType ) settings . lightType . enumValueIndex )
switch ( ( LightType ) m_BaseData . type . enumValueIndex )
{
case LightType . Directional :
EditorGUILayout . Slider ( m_BaseData . bakedShadowAngle , 0f , 9 0f , s_Styles . bakedShadowAngle ) ;
break ;
case LightType . Spot :
case LightType . Point :
EditorGUILayout . PropertyField ( m_BaseData . bakedShadowRadius , s_Styles . bakedShadowRadius ) ;
break ;
}
case LightType . Directional :
EditorGUILayout . Slider ( settings . bakedShadowAngleProp , 0f , 9 0f , s_Styles . bakedShadowAngle ) ;
break ;
case LightType . Spot :
case LightType . Point :
EditorGUILayout . PropertyField ( settings . bakedShadowRadiusProp , s_Styles . bakedShadowRadius ) ;
break ;
}
}
void DrawShadows ( )
{
if ( settings . isCompletelyBaked )
{
DrawBakedShadowParameters ( ) ;
EditorGUILayout . Slider ( m_BaseData . shadowsBias , 0.001f , 1f , s_Styles . shadowBias ) ;
EditorGUILayout . Slider ( m_BaseData . shadowsNormalBias , 0.001f , 1f , s_Styles . shadowNormalBias ) ;
EditorGUILayout . Slider ( m_BaseData . shadowsNearPlane , 0.01f , 1 0f , s_Styles . shadowNearPlane ) ;
if ( m_BaseData . type . enumValueIndex ! = ( int ) LightType . Directional )
return ;
EditorGUILayout . Slider ( settings . shadowsBias , 0.001f , 1f , s_Styles . shadowBias ) ;
EditorGUILayout . Slider ( settings . shadowsNormalBias , 0.001f , 1f , s_Styles . shadowNormalBias ) ;
EditorGUILayout . Slider ( settings . shadowsNearPlane , 0.01f , 1 0f , s_Styles . shadowNearPlane ) ;
using ( var scope = new EditorGUI . ChangeCheckScope ( ) )
if ( settings . lightType . enumValueIndex = = ( int ) LightType . Directional )
EditorGUILayout . IntSlider ( m_AdditionalShadowData . cascadeCount , 1 , 4 , s_Styles . shadowCascadeCount ) ;
if ( scope . changed )
using ( var scope = new EditorGUI . ChangeCheckScope ( ) )
int len = m_AdditionalShadowData . cascadeCount . intValue ;
m_AdditionalShadowData . cascadeRatios . arraySize = len - 1 ;
m_AdditionalShadowData . cascadeBorders . arraySize = len ;
EditorGUILayout . IntSlider ( m_AdditionalShadowData . cascadeCount , 1 , 4 , s_Styles . shadowCascadeCount ) ;
if ( scope . changed )
{
int len = m_AdditionalShadowData . cascadeCount . intValue ;
m_AdditionalShadowData . cascadeRatios . arraySize = len - 1 ;
m_AdditionalShadowData . cascadeBorders . arraySize = len ;
}
}
EditorGUI . indentLevel + + ;
EditorGUI . indentLevel + + ;
using ( var scope = new EditorGUI . ChangeCheckScope ( ) )
{
// Draw each field first...
int arraySize = m_AdditionalShadowData . cascadeRatios . arraySize ;
for ( int i = 0 ; i < arraySize ; i + + )
EditorGUILayout . Slider ( m_AdditionalShadowData . cascadeRatios . GetArrayElementAtIndex ( i ) , 0f , 1f , s_Styles . shadowCascadeRatios [ i ] ) ;
if ( scope . changed )
using ( var scope = new EditorGUI . ChangeCheckScope ( ) )
// ...then clamp values to avoid out of bounds cascade ratios
// Draw each field first...
int arraySize = m_AdditionalShadowData . cascadeRatios . arraySize ;
{
var ratios = m_AdditionalShadowData . cascadeRatios ;
var ratioProp = ratios . GetArrayElementAtIndex ( i ) ;
float val = ratioProp . floatValue ;
EditorGUILayout . Slider ( m_AdditionalShadowData . cascadeRatios . GetArrayElementAtIndex ( i ) , 0f , 1f , s_Styles . shadowCascadeRatios [ i ] ) ;
if ( i > 0 )
if ( scope . changed )
{
// ...then clamp values to avoid out of bounds cascade ratios
for ( int i = 0 ; i < arraySize ; i + + )
var prevRatioProp = ratios . GetArrayElementAtIndex ( i - 1 ) ;
float prevVal = prevRatioProp . floatValue ;
val = Mathf . Max ( val , prevVal ) ;
}
var ratios = m_AdditionalShadowData . cascadeRatios ;
var ratioProp = ratios . GetArrayElementAtIndex ( i ) ;
float val = ratioProp . floatValue ;
if ( i > 0 )
{
var prevRatioProp = ratios . GetArrayElementAtIndex ( i - 1 ) ;
float prevVal = prevRatioProp . floatValue ;
val = Mathf . Max ( val , prevVal ) ;
}
if ( i < arraySize - 1 )
{
var nextRatioProp = ratios . GetArrayElementAtIndex ( i + 1 ) ;
float nextVal = nextRatioProp . floatValue ;
val = Mathf . Min ( val , nextVal ) ;
}
if ( i < arraySize - 1 )
{
var nextRatioProp = ratios . GetArrayElementAtIndex ( i + 1 ) ;
float nextVal = nextRatioProp . floatValue ;
val = Mathf . Min ( val , nextVal ) ;
ratioProp . floatValue = val ;
ratioProp . floatValue = val ;
EditorGUI . indentLevel - - ;
EditorGUI . indentLevel - - ;
if ( settings . isBakedOrMixed )
DrawBakedShadowParameters ( ) ;
EditorGUILayout . Space ( ) ;
EditorGUILayout . PropertyField ( m_AdditionalShadowData . fadeDistance , s_Styles . shadowFadeDistance ) ;
if ( settings . lightType . enumValueIndex = = ( int ) LightType . Point | | settings . lightType . enumValueIndex = = ( int ) LightType . Spot )
EditorGUILayout . PropertyField ( m_AdditionalShadowData . fadeDistance , s_Styles . shadowFadeDistance ) ;
EditorGUILayout . PropertyField ( m_AdditionalShadowData . dimmer , s_Styles . shadowDimmer ) ;
EditorGUI . indentLevel - - ;
}
void ResolveLightShape ( )
{
var type = m_BaseData . t ype;
var type = settings . lightT ype;
// Special case for multi-selection: don't resolve light shape or it'll corrupt lights
if ( type . hasMultipleDifferentValues )
break ;
}
}
}
// TODO: Move this to a generic EditorUtilities class
T [ ] GetAdditionalData < T > ( )
where T : Component
{
// Handles multi-selection
var data = targets . Cast < Component > ( )
. Select ( t = > t . GetComponent < T > ( ) )
. ToArray ( ) ;
for ( int i = 0 ; i < data . Length ; i + + )
{
if ( data [ i ] = = null )
data [ i ] = Undo . AddComponent < T > ( ( ( Component ) targets [ i ] ) . gameObject ) ;
}
return data ;
}
}
}