using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using UnityEditorInternal;
using UnityEngine.Rendering;
using UnityEngine.Rendering.HighDefinition;
using System.Linq;
using System;
using System.Reflection;
namespace UnityEditor.Rendering.HighDefinition
{
///
/// Custom UI class for custom passes
///
[CustomPassDrawerAttribute(typeof(CustomPass))]
public class CustomPassDrawer
{
class Styles
{
public static float defaultLineSpace = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
public static float reorderableListHandleIndentWidth = 12;
public static GUIContent callback = new GUIContent("Event", "Chose the Callback position for this render pass object.");
public static GUIContent enabled = new GUIContent("Enabled", "Enable or Disable the custom pass");
public static GUIContent targetDepthBuffer = new GUIContent("Target Depth Buffer");
public static GUIContent targetColorBuffer = new GUIContent("Target Color Buffer");
public static GUIContent clearFlags = new GUIContent("Clear Flags", "Clear Flags used when the render targets will are bound, before the pass renders.");
}
///
/// List of the elements you can show/hide in the default custom pass UI.
///
[Flags]
public enum PassUIFlag
{
/// Hides all the default UI fields.
None = 0x00,
/// Shows the name field.
Name = 0x01,
/// Shows the target color buffer field.
TargetColorBuffer = 0x02,
/// Shows the target depth buffer field.
TargetDepthBuffer = 0x04,
/// Shows the clear flags field.
ClearFlags = 0x08,
/// Shows all the default UI fields.
All = ~0,
}
///
/// Controls which field of the common pass UI is displayed.
///
protected virtual PassUIFlag commonPassUIFlags => PassUIFlag.All;
///
/// Get the current instance of the custom pass being displayed in the Custom Pass Volume Editor.
///
protected CustomPass target => m_CustomPass;
bool firstTime = true;
// Serialized Properties
SerializedProperty m_Name;
SerializedProperty m_Enabled;
SerializedProperty m_TargetColorBuffer;
SerializedProperty m_TargetDepthBuffer;
SerializedProperty m_ClearFlags;
SerializedProperty m_PassFoldout;
List m_CustomPassUserProperties = new List();
CustomPass m_CustomPass;
Type m_PassType => m_CustomPass.GetType();
void FetchProperties(SerializedProperty property)
{
m_Name = property.FindPropertyRelative("m_Name");
m_Enabled = property.FindPropertyRelative("enabled");
m_TargetColorBuffer = property.FindPropertyRelative("targetColorBuffer");
m_TargetDepthBuffer = property.FindPropertyRelative("targetDepthBuffer");
m_ClearFlags = property.FindPropertyRelative("clearFlags");
m_PassFoldout = property.FindPropertyRelative("passFoldout");
}
void LoadUserProperties(SerializedProperty customPass)
{
// Store all fields in CustomPass so we can exclude them when retrieving the user custom pass type
var customPassFields = typeof(CustomPass).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
m_CustomPassUserProperties.Clear();
foreach (var field in m_PassType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
var serializeField = field.GetCustomAttribute();
var hideInInspector = field.GetCustomAttribute();
var nonSerialized = field.GetCustomAttribute();
if (customPassFields.Any(f => f.Name == field.Name))
continue;
if (nonSerialized != null || hideInInspector != null)
continue;
if (!field.IsPublic && serializeField == null)
continue;
var prop = customPass.FindPropertyRelative(field.Name);
if (prop != null)
m_CustomPassUserProperties.Add(prop);
}
}
void InitInternal(SerializedProperty customPass)
{
FetchProperties(customPass);
Initialize(customPass);
LoadUserProperties(customPass);
firstTime = false;
}
///
/// Use this function to initialize the local SerializedProperty you will use in your pass.
///
/// Your custom pass instance represented as a SerializedProperty
protected virtual void Initialize(SerializedProperty customPass) { }
internal void SetPass(CustomPass pass) => m_CustomPass = pass;
internal void OnGUI(Rect rect, SerializedProperty property, GUIContent label)
{
rect.height = EditorGUIUtility.singleLineHeight;
EditorGUI.BeginChangeCheck();
if (firstTime)
InitInternal(property);
DoHeaderGUI(ref rect);
if (m_PassFoldout.boolValue)
{
EditorGUI.EndChangeCheck();
return;
}
EditorGUI.BeginDisabledGroup(!m_Enabled.boolValue);
{
DoCommonSettingsGUI(ref rect);
DoPassGUI(property, rect);
}
EditorGUI.EndDisabledGroup();
if (EditorGUI.EndChangeCheck())
property.serializedObject.ApplyModifiedProperties();
}
void DoCommonSettingsGUI(ref Rect rect)
{
if ((commonPassUIFlags & PassUIFlag.Name) != 0)
{
EditorGUI.BeginChangeCheck();
EditorGUI.PropertyField(rect, m_Name);
if (EditorGUI.EndChangeCheck())
m_CustomPass.name = m_Name.stringValue;
rect.y += Styles.defaultLineSpace;
}
if ((commonPassUIFlags & PassUIFlag.TargetColorBuffer) != 0)
{
EditorGUI.PropertyField(rect, m_TargetColorBuffer, Styles.targetColorBuffer);
rect.y += Styles.defaultLineSpace;
}
if ((commonPassUIFlags & PassUIFlag.TargetDepthBuffer) != 0)
{
EditorGUI.PropertyField(rect, m_TargetDepthBuffer, Styles.targetDepthBuffer);
rect.y += Styles.defaultLineSpace;
}
if ((commonPassUIFlags & PassUIFlag.ClearFlags) != 0)
{
EditorGUI.PropertyField(rect, m_ClearFlags, Styles.clearFlags);
rect.y += Styles.defaultLineSpace;
}
}
///
/// Implement this function to draw your custom GUI.
///
/// Your custom pass instance represented as a SerializedProperty
/// space available for you to draw the UI
protected virtual void DoPassGUI(SerializedProperty customPass, Rect rect)
{
foreach (var prop in m_CustomPassUserProperties)
{
EditorGUI.PropertyField(rect, prop, true);
rect.y += EditorGUI.GetPropertyHeight(prop) + EditorGUIUtility.standardVerticalSpacing;
}
}
void DoHeaderGUI(ref Rect rect)
{
var enabledSize = EditorStyles.boldLabel.CalcSize(Styles.enabled) + new Vector2(Styles.reorderableListHandleIndentWidth, 0);
var headerRect = new Rect(rect.x + Styles.reorderableListHandleIndentWidth,
rect.y + EditorGUIUtility.standardVerticalSpacing,
rect.width - Styles.reorderableListHandleIndentWidth - enabledSize.x,
EditorGUIUtility.singleLineHeight);
rect.y += Styles.defaultLineSpace;
var enabledRect = headerRect;
enabledRect.x = rect.xMax - enabledSize.x;
enabledRect.width = enabledSize.x;
EditorGUI.BeginProperty(headerRect, GUIContent.none, m_PassFoldout);
{
EditorGUIUtility.labelWidth = headerRect.width;
m_PassFoldout.boolValue = EditorGUI.Foldout(headerRect, m_PassFoldout.boolValue, $"{m_Name.stringValue} ({m_PassType.Name})", true, EditorStyles.boldLabel);
EditorGUIUtility.labelWidth = 0;
}
EditorGUI.EndProperty();
EditorGUI.BeginProperty(enabledRect, Styles.enabled, m_Enabled);
{
EditorGUIUtility.labelWidth = enabledRect.width - 14;
m_Enabled.boolValue = EditorGUI.Toggle(enabledRect, Styles.enabled, m_Enabled.boolValue);
EditorGUIUtility.labelWidth = 0;
}
EditorGUI.EndProperty();
}
///
/// Implement this functions if you implement DoPassGUI. The result of this function must match the number of lines displayed in your custom GUI.
/// Note that this height can be dynamic.
///
/// Your custom pass instance represented as a SerializedProperty
/// The height in pixels of tour custom pass GUI
protected virtual float GetPassHeight(SerializedProperty customPass)
{
float height = 0;
foreach (var prop in m_CustomPassUserProperties)
{
height += EditorGUI.GetPropertyHeight(prop);
height += EditorGUIUtility.standardVerticalSpacing;
}
return height;
}
internal float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
float height = Styles.defaultLineSpace;
if (firstTime)
InitInternal(property);
if (m_PassFoldout.boolValue)
return height;
if (!firstTime)
{
int lines = 4; // name + target buffers + clearFlags
if (commonPassUIFlags != PassUIFlag.All)
{
lines = 0;
for (int i = 0; i < 32; i++)
lines += (((int)commonPassUIFlags & (1 << i)) != 0) ? 1 : 0;
}
height += Styles.defaultLineSpace * lines;
}
return height + GetPassHeight(property);
}
internal GUIContent[] GetMaterialPassNames(Material mat)
{
GUIContent[] passNames = new GUIContent[mat.passCount];
for (int i = 0; i < mat.passCount; i++)
{
string passName = mat.GetPassName(i);
passNames[i] = new GUIContent(string.IsNullOrEmpty(passName) ? i.ToString() : passName);
}
return passNames;
}
}
}