return value ;
}
public static void DrawPopup ( GUIContent label , SerializedProperty property , string [ ] options )
{
var mode = property . intValue ;
EditorGUI . BeginChangeCheck ( ) ;
if ( mode > = options . Length )
Debug . LogError ( string . Format ( "Invalid option while trying to set {0}" , label . text ) ) ;
mode = EditorGUILayout . Popup ( label , mode , options ) ;
if ( EditorGUI . EndChangeCheck ( ) )
{
Undo . RecordObject ( property . objectReferenceValue , property . name ) ;
property . intValue = mode ;
}
}
public static void DrawCascadeSplitGUI < T > ( ref SerializedProperty shadowCascadeSplit )
{
float [ ] cascadePartitionSizes = null ;
System . Type type = typeof ( T ) ;
if ( type = = typeof ( float ) )
{
cascadePartitionSizes = new float [ ] { shadowCascadeSplit . floatValue } ;
}
else if ( type = = typeof ( Vector3 ) )
{
Vector3 splits = shadowCascadeSplit . vector3Value ;
cascadePartitionSizes = new float [ ]
{
Mathf . Clamp ( splits [ 0 ] , 0.0f , 1.0f ) ,
Mathf . Clamp ( splits [ 1 ] - splits [ 0 ] , 0.0f , 1.0f ) ,
Mathf . Clamp ( splits [ 2 ] - splits [ 1 ] , 0.0f , 1.0f )
} ;
}
if ( cascadePartitionSizes ! = null )
{
EditorGUI . BeginChangeCheck ( ) ;
ShadowCascadeSplitGUI . HandleCascadeSliderGUI ( ref cascadePartitionSizes ) ;
if ( EditorGUI . EndChangeCheck ( ) )
{
if ( type = = typeof ( float ) )
shadowCascadeSplit . floatValue = cascadePartitionSizes [ 0 ] ;
else
{
Vector3 updatedValue = new Vector3 ( ) ;
updatedValue [ 0 ] = cascadePartitionSizes [ 0 ] ;
updatedValue [ 1 ] = updatedValue [ 0 ] + cascadePartitionSizes [ 1 ] ;
updatedValue [ 2 ] = updatedValue [ 1 ] + cascadePartitionSizes [ 2 ] ;
shadowCascadeSplit . vector3Value = updatedValue ;
}
}
}
}
public static void RemoveMaterialKeywords ( Material material )
{
material . shaderKeywords = null ;
}
return data ;
}
}
static class ShadowCascadeSplitGUI
{
private const int kSliderbarTopMargin = 2 ;
private const int kSliderbarHeight = 2 4 ;
private const int kSliderbarBottomMargin = 2 ;
private const int kPartitionHandleWidth = 2 ;
private const int kPartitionHandleExtraHitAreaWidth = 2 ;
private static readonly Color [ ] kCascadeColors =
{
new Color ( 0.5f , 0.5f , 0.6f , 1.0f ) ,
new Color ( 0.5f , 0.6f , 0.5f , 1.0f ) ,
new Color ( 0.6f , 0.6f , 0.5f , 1.0f ) ,
new Color ( 0.6f , 0.5f , 0.5f , 1.0f ) ,
} ;
// using a LODGroup skin
private static readonly GUIStyle s_CascadeSliderBG = "LODSliderRange" ;
private static readonly GUIStyle s_TextCenteredStyle = new GUIStyle ( EditorStyles . whiteMiniLabel )
{
alignment = TextAnchor . MiddleCenter
} ;
// Internal struct to bundle drag information
private class DragCache
{
public int m_ActivePartition ; // the cascade partition that we are currently dragging/resizing
public float m_NormalizedPartitionSize ; // the normalized size of the partition (0.0f < size < 1.0f)
public Vector2 m_LastCachedMousePosition ; // mouse position the last time we registered a drag or mouse down.
public DragCache ( int activePartition , float normalizedPartitionSize , Vector2 currentMousePos )
{
m_ActivePartition = activePartition ;
m_NormalizedPartitionSize = normalizedPartitionSize ;
m_LastCachedMousePosition = currentMousePos ;
}
} ;
private static DragCache s_DragCache ;
private static readonly int s_CascadeSliderId = "s_CascadeSliderId" . GetHashCode ( ) ;
private static SceneView s_RestoreSceneView ;
private static SceneView . CameraMode s_OldSceneDrawMode ;
private static bool s_OldSceneLightingMode ;
/ * *
* Static function to handle the GUI and User input related to the cascade slider .
*
* @param normalizedCascadePartition The array of partition sizes in the range 0.0f - 1.0f ; expects ONE entry if cascades = 2 , and THREE if cascades = 4
* The last entry will be automatically determined by summing up the array , and doing 1.0f - sum
* /
public static void HandleCascadeSliderGUI ( ref float [ ] normalizedCascadePartitions )
{
EditorGUILayout . LabelField ( "Cascade splits" ) ;
// get the inspector width since we need it while drawing the partition rects.
// Only way currently is to reserve the block in the layout using GetRect(), and then immediately drawing the empty box
// to match the call to GetRect.
// From this point on, we move to non-layout based code.
var sliderRect = GUILayoutUtility . GetRect ( GUIContent . none
, s_CascadeSliderBG
, GUILayout . Height ( kSliderbarTopMargin + kSliderbarHeight + kSliderbarBottomMargin )
, GUILayout . ExpandWidth ( true ) ) ;
GUI . Box ( sliderRect , GUIContent . none ) ;
float currentX = sliderRect . x ;
float cascadeBoxStartY = sliderRect . y + kSliderbarTopMargin ;
float cascadeSliderWidth = sliderRect . width - ( normalizedCascadePartitions . Length * kPartitionHandleWidth ) ;
Color origTextColor = GUI . color ;
Color origBackgroundColor = GUI . backgroundColor ;
int colorIndex = - 1 ;
// setup the array locally with the last partition
float [ ] adjustedCascadePartitions = new float [ normalizedCascadePartitions . Length + 1 ] ;
System . Array . Copy ( normalizedCascadePartitions , adjustedCascadePartitions , normalizedCascadePartitions . Length ) ;
adjustedCascadePartitions [ adjustedCascadePartitions . Length - 1 ] = 1.0f - normalizedCascadePartitions . Sum ( ) ;
// check for user input on any of the partition handles
// this mechanism gets the current event in the queue... make sure that the mouse is over our control before consuming the event
int sliderControlId = GUIUtility . GetControlID ( s_CascadeSliderId , FocusType . Passive ) ;
Event currentEvent = Event . current ;
int hotPartitionHandleIndex = - 1 ; // the index of any partition handle that we are hovering over or dragging
// draw each cascade partition
for ( int i = 0 ; i < adjustedCascadePartitions . Length ; + + i )
{
float currentPartition = adjustedCascadePartitions [ i ] ;
colorIndex = ( colorIndex + 1 ) % kCascadeColors . Length ;
GUI . backgroundColor = kCascadeColors [ colorIndex ] ;
float boxLength = ( cascadeSliderWidth * currentPartition ) ;
// main cascade box
Rect partitionRect = new Rect ( currentX , cascadeBoxStartY , boxLength , kSliderbarHeight ) ;
GUI . Box ( partitionRect , GUIContent . none , s_CascadeSliderBG ) ;
currentX + = boxLength ;
// cascade box percentage text
GUI . color = Color . white ;
Rect textRect = partitionRect ;
var cascadeText = string . Format ( "{0}\n{1:F1}%" , i , currentPartition * 1 0 0.0f ) ;
GUI . Label ( textRect , cascadeText , s_TextCenteredStyle ) ;
// no need to draw the partition handle for last box
if ( i = = adjustedCascadePartitions . Length - 1 )
break ;
// partition handle
GUI . backgroundColor = Color . black ;
Rect handleRect = partitionRect ;
handleRect . x = currentX ;
handleRect . width = kPartitionHandleWidth ;
GUI . Box ( handleRect , GUIContent . none , s_CascadeSliderBG ) ;
// we want a thin handle visually (since wide black bar looks bad), but a slightly larger
// hit area for easier manipulation
Rect handleHitRect = handleRect ;
handleHitRect . xMin - = kPartitionHandleExtraHitAreaWidth ;
handleHitRect . xMax + = kPartitionHandleExtraHitAreaWidth ;
if ( handleHitRect . Contains ( currentEvent . mousePosition ) )
hotPartitionHandleIndex = i ;
// add regions to slider where the cursor changes to Resize-Horizontal
if ( s_DragCache = = null )
{
EditorGUIUtility . AddCursorRect ( handleHitRect , MouseCursor . ResizeHorizontal , sliderControlId ) ;
}
currentX + = kPartitionHandleWidth ;
}
GUI . color = origTextColor ;
GUI . backgroundColor = origBackgroundColor ;
EventType eventType = currentEvent . GetTypeForControl ( sliderControlId ) ;
switch ( eventType )
{
case EventType . MouseDown :
if ( hotPartitionHandleIndex > = 0 )
{
s_DragCache = new DragCache ( hotPartitionHandleIndex , normalizedCascadePartitions [ hotPartitionHandleIndex ] , currentEvent . mousePosition ) ;
if ( GUIUtility . hotControl = = 0 )
GUIUtility . hotControl = sliderControlId ;
currentEvent . Use ( ) ;
// Switch active scene view into shadow cascades visualization mode, once we start
// tweaking cascade splits.
if ( s_RestoreSceneView = = null )
{
s_RestoreSceneView = SceneView . lastActiveSceneView ;
if ( s_RestoreSceneView ! = null )
{
s_OldSceneDrawMode = s_RestoreSceneView . cameraMode ;
s_OldSceneLightingMode = s_RestoreSceneView . m_SceneLighting ;
s_RestoreSceneView . cameraMode = SceneView . GetBuiltinCameraMode ( DrawCameraMode . ShadowCascades ) ;
}
}
}
break ;
case EventType . MouseUp :
// mouseUp event anywhere should release the hotcontrol (if it belongs to us), drags (if any)
if ( GUIUtility . hotControl = = sliderControlId )
{
GUIUtility . hotControl = 0 ;
currentEvent . Use ( ) ;
}
s_DragCache = null ;
// Restore previous scene view drawing mode once we stop tweaking cascade splits.
if ( s_RestoreSceneView ! = null )
{
s_RestoreSceneView . cameraMode = s_OldSceneDrawMode ;
s_RestoreSceneView . m_SceneLighting = s_OldSceneLightingMode ;
s_RestoreSceneView = null ;
}
break ;
case EventType . MouseDrag :
if ( GUIUtility . hotControl ! = sliderControlId )
break ;
// convert the mouse movement to normalized cascade width. Make sure that we are safe to apply the delta before using it.
float delta = ( currentEvent . mousePosition - s_DragCache . m_LastCachedMousePosition ) . x / cascadeSliderWidth ;
bool isLeftPartitionHappy = ( ( adjustedCascadePartitions [ s_DragCache . m_ActivePartition ] + delta ) > 0.0f ) ;
bool isRightPartitionHappy = ( ( adjustedCascadePartitions [ s_DragCache . m_ActivePartition + 1 ] - delta ) > 0.0f ) ;
if ( isLeftPartitionHappy & & isRightPartitionHappy )
{
s_DragCache . m_NormalizedPartitionSize + = delta ;
normalizedCascadePartitions [ s_DragCache . m_ActivePartition ] = s_DragCache . m_NormalizedPartitionSize ;
if ( s_DragCache . m_ActivePartition < normalizedCascadePartitions . Length - 1 )
normalizedCascadePartitions [ s_DragCache . m_ActivePartition + 1 ] - = delta ;
GUI . changed = true ;
}
s_DragCache . m_LastCachedMousePosition = currentEvent . mousePosition ;
currentEvent . Use ( ) ;
break ;
}
}
}
}