diff --git a/CHANGELOG.md b/CHANGELOG.md index 016199c..8c8b0d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [4.2.0-preview] - 2018-11-16 + +### Added +- Added a define for determining if any instancing path is taken. + ## [4.1.0-preview] - 2018-10-18 ### Changed diff --git a/Editor/CoreEditorDrawers.cs b/Editor/CoreEditorDrawers.cs index ba46ba3..3a9f14b 100644 --- a/Editor/CoreEditorDrawers.cs +++ b/Editor/CoreEditorDrawers.cs @@ -10,11 +10,15 @@ public enum FoldoutOption { None = 0, Indent = 1 << 0, + [Obsolete("animated inspector does not been approved by UX team")] Animate = 1 << 1, - Boxed = 1 << 2 + Boxed = 1 << 2, + SubFoldout = 1 << 3, + NoSpaceAtEnd = 1 << 4 } [Flags] + [Obsolete("Will disappear with CoreEditorDrawer")] public enum FadeOption { None = 0, @@ -22,6 +26,14 @@ public enum FadeOption Animate = 1 << 1 } + [Flags] + public enum GroupOption + { + None = 0, + Indent = 1 << 0 + } + + [Obsolete("Prefer using the version without the UIState in coordination with a static ExpandedState")] public static class CoreEditorDrawer { public interface IDrawer @@ -37,7 +49,7 @@ public interface IDrawer public delegate AnimBool AnimBoolGetter(TUIState s, TData p, Editor owner); public static readonly IDrawer space = Action((state, data, owner) => EditorGUILayout.Space()); - public static readonly IDrawer noop = Action((state, data, owner) => {}); + public static readonly IDrawer noop = Action((state, data, owner) => { }); public static IDrawer Group(params IDrawer[] drawers) { @@ -47,13 +59,13 @@ public static IDrawer Group(params IDrawer[] drawers) public static IDrawer LabelWidth(float width, params IDrawer[] drawers) { return Action((s, d, o) => - { - var l = EditorGUIUtility.labelWidth; - EditorGUIUtility.labelWidth = width; - for (var i = 0; i < drawers.Length; ++i) - drawers[i].Draw(s, d, o); - EditorGUIUtility.labelWidth = l; - } + { + var l = EditorGUIUtility.labelWidth; + EditorGUIUtility.labelWidth = width; + for (var i = 0; i < drawers.Length; ++i) + drawers[i].Draw(s, d, o); + EditorGUIUtility.labelWidth = l; + } ); } @@ -216,14 +228,475 @@ public void Draw(TUIState s, TData p, Editor owner) GUILayout.EndVertical(); } } + + /// Create an IDrawer foldout header using an ExpandedState + /// Title wanted for this foldout header + /// Bit mask (enum) used to define the boolean saving the state in ExpandedState + /// The ExpandedState describing the component + /// The content of the foldout header + public static IDrawer FoldoutGroup(string title, TEnum mask, ExpandedState state, params ActionDrawer[] contentDrawer) + where TEnum : struct, IConvertible + { + return Action((uiState, data, editor) => + { + CoreEditorUtils.DrawSplitter(); + bool expended = state[mask]; + bool newExpended = CoreEditorUtils.DrawHeaderFoldout(title, expended); + if (newExpended ^ expended) + state[mask] = newExpended; + if (newExpended) + { + ++EditorGUI.indentLevel; + for (var i = 0; i < contentDrawer.Length; i++) + contentDrawer[i](uiState, data, editor); + --EditorGUI.indentLevel; + EditorGUILayout.Space(); + } + }); + } + + /// Create an IDrawer foldout header using an ExpandedState + /// Title wanted for this foldout header + /// Bit mask (enum) used to define the boolean saving the state in ExpandedState + /// The ExpandedState describing the component + /// The content of the foldout header + public static IDrawer FoldoutGroup(string title, TEnum mask, ExpandedState state, params IDrawer[] contentDrawer) + where TEnum : struct, IConvertible + { + return FoldoutGroup(title, mask, state, contentDrawer.Draw); + } + } + + /// + /// Utility class to draw inspectors + /// + /// Type of class containing data needed to draw inspector + public static class CoreEditorDrawer + { + /// Abstraction that have the Draw hability + public interface IDrawer + { + void Draw(TData p, Editor owner); + } + + public delegate bool Enabler(TData data, Editor owner); + public delegate void SwitchEnabler(TData data, Editor owner); + public delegate T2Data DataSelect(TData data, Editor owner); + public delegate void ActionDrawer(TData data, Editor owner); + + /// Equivalent to EditorGUILayout.Space that can be put in a drawer group + public static readonly IDrawer space = Group((data, owner) => EditorGUILayout.Space()); + + /// Use it when IDrawer required but no operation should be done + public static readonly IDrawer noop = Group((data, owner) => { }); + + /// + /// Conditioned drawer that will only be drawn if its enabler function is null or return true + /// + /// Enable the drawing if null or return true + /// The content of the group + public static IDrawer Conditional(Enabler enabler, params IDrawer[] contentDrawers) + { + return new ConditionalDrawerInternal(enabler, contentDrawers.Draw); + } + + /// + /// Conditioned drawer that will only be drawn if its enabler function is null or return true + /// + /// Enable the drawing if null or return true + /// The content of the group + public static IDrawer Conditional(Enabler enabler, params ActionDrawer[] contentDrawers) + { + return new ConditionalDrawerInternal(enabler, contentDrawers); + } + + class ConditionalDrawerInternal : IDrawer + { + ActionDrawer[] actionDrawers { get; set; } + Enabler m_Enabler; + + public ConditionalDrawerInternal(Enabler enabler = null, params ActionDrawer[] actionDrawers) + { + this.actionDrawers = actionDrawers; + m_Enabler = enabler; + } + + void IDrawer.Draw(TData data, Editor owner) + { + if (m_Enabler != null && !m_Enabler(data, owner)) + return; + + for (var i = 0; i < actionDrawers.Length; i++) + actionDrawers[i](data, owner); + } + } + + /// + /// Group of drawing function for inspector. + /// They will be drawn one after the other. + /// + /// The content of the group + public static IDrawer Group(params IDrawer[] contentDrawers) + { + return new GroupDrawerInternal(-1f, GroupOption.None, contentDrawers.Draw); + } + + /// + /// Group of drawing function for inspector. + /// They will be drawn one after the other. + /// + /// The content of the group + public static IDrawer Group(params ActionDrawer[] contentDrawers) + { + return new GroupDrawerInternal(-1f, GroupOption.None, contentDrawers); + } + + /// Group of drawing function for inspector with a set width for labels + /// Width used for all labels in the group + /// The content of the group + public static IDrawer Group(float labelWidth, params IDrawer[] contentDrawers) + { + return new GroupDrawerInternal(labelWidth, GroupOption.None, contentDrawers.Draw); + } + + /// Group of drawing function for inspector with a set width for labels + /// Width used for all labels in the group + /// The content of the group + public static IDrawer Group(float labelWidth, params ActionDrawer[] contentDrawers) + { + return new GroupDrawerInternal(labelWidth, GroupOption.None, contentDrawers); + } + + /// + /// Group of drawing function for inspector. + /// They will be drawn one after the other. + /// + /// Allow to add indentation on this group + /// The content of the group + public static IDrawer Group(GroupOption options, params IDrawer[] contentDrawers) + { + return new GroupDrawerInternal(-1f, options, contentDrawers.Draw); + } + + /// + /// Group of drawing function for inspector. + /// They will be drawn one after the other. + /// + /// Allow to add indentation on this group + /// The content of the group + public static IDrawer Group(GroupOption options, params ActionDrawer[] contentDrawers) + { + return new GroupDrawerInternal(-1f, options, contentDrawers); + } + + /// Group of drawing function for inspector with a set width for labels + /// Width used for all labels in the group + /// Allow to add indentation on this group + /// The content of the group + public static IDrawer Group(float labelWidth, GroupOption options, params IDrawer[] contentDrawers) + { + return new GroupDrawerInternal(labelWidth, options, contentDrawers.Draw); + } + + /// Group of drawing function for inspector with a set width for labels + /// Width used for all labels in the group + /// Allow to add indentation on this group + /// The content of the group + public static IDrawer Group(float labelWidth, GroupOption options, params ActionDrawer[] contentDrawers) + { + return new GroupDrawerInternal(labelWidth, options, contentDrawers); + } + + class GroupDrawerInternal : IDrawer + { + ActionDrawer[] actionDrawers { get; set; } + float m_LabelWidth; + bool isIndented; + + public GroupDrawerInternal(float labelWidth = -1f, GroupOption options = GroupOption.None, params ActionDrawer[] actionDrawers) + { + this.actionDrawers = actionDrawers; + m_LabelWidth = labelWidth; + isIndented = (options & GroupOption.Indent) != 0; + } + + void IDrawer.Draw(TData data, Editor owner) + { + if (isIndented) + ++EditorGUI.indentLevel; + var currentLabelWidth = EditorGUIUtility.labelWidth; + if (m_LabelWidth >= 0f) + { + EditorGUIUtility.labelWidth = m_LabelWidth; + } + for (var i = 0; i < actionDrawers.Length; i++) + actionDrawers[i](data, owner); + if (m_LabelWidth >= 0f) + { + EditorGUIUtility.labelWidth = currentLabelWidth; + } + if (isIndented) + --EditorGUI.indentLevel; + } + } + + /// Create an IDrawer based on an other data container + /// The data new source for the inner drawers + /// Inner drawers drawed with given data sources + /// + public static IDrawer Select( + DataSelect dataSelect, + params CoreEditorDrawer.IDrawer[] otherDrawers) + { + return new SelectDrawerInternal(dataSelect, otherDrawers.Draw); + } + + /// Create an IDrawer based on an other data container + /// The data new source for the inner drawers + /// Inner drawers drawed with given data sources + /// + public static IDrawer Select( + DataSelect dataSelect, + params CoreEditorDrawer.ActionDrawer[] otherDrawers) + { + return new SelectDrawerInternal(dataSelect, otherDrawers); + } + + class SelectDrawerInternal : IDrawer + { + DataSelect m_DataSelect; + CoreEditorDrawer.ActionDrawer[] m_SourceDrawers; + + public SelectDrawerInternal(DataSelect dataSelect, + params CoreEditorDrawer.ActionDrawer[] otherDrawers) + { + m_SourceDrawers = otherDrawers; + m_DataSelect = dataSelect; + } + + void IDrawer.Draw(TData data, Editor o) + { + var p2 = m_DataSelect(data, o); + for (var i = 0; i < m_SourceDrawers.Length; i++) + m_SourceDrawers[i](p2, o); + } + } + + /// + /// Create an IDrawer foldout header using an ExpandedState. + /// The default option is Indent in this version. + /// + /// Title wanted for this foldout header + /// Bit mask (enum) used to define the boolean saving the state in ExpandedState + /// The ExpandedState describing the component + /// The content of the foldout header + public static IDrawer FoldoutGroup(string title, TEnum mask, ExpandedState state, params IDrawer[] contentDrawers) + where TEnum : struct, IConvertible + { + return FoldoutGroup(title, mask, state, contentDrawers.Draw); + } + + /// + /// Create an IDrawer foldout header using an ExpandedState. + /// The default option is Indent in this version. + /// + /// Title wanted for this foldout header + /// Bit mask (enum) used to define the boolean saving the state in ExpandedState + /// The ExpandedState describing the component + /// The content of the foldout header + public static IDrawer FoldoutGroup(string title, TEnum mask, ExpandedState state, params ActionDrawer[] contentDrawers) + where TEnum : struct, IConvertible + { + return FoldoutGroup(CoreEditorUtils.GetContent(title), mask, state, contentDrawers); + } + + /// Create an IDrawer foldout header using an ExpandedState + /// Title wanted for this foldout header + /// Bit mask (enum) used to define the boolean saving the state in ExpandedState + /// The ExpandedState describing the component + /// The content of the foldout header + public static IDrawer FoldoutGroup(string title, TEnum mask, ExpandedState state, FoldoutOption options, params IDrawer[] contentDrawers) + where TEnum : struct, IConvertible + { + return FoldoutGroup(title, mask, state, options, contentDrawers.Draw); + } + + /// Create an IDrawer foldout header using an ExpandedState + /// Title wanted for this foldout header + /// Bit mask (enum) used to define the boolean saving the state in ExpandedState + /// The ExpandedState describing the component + /// The content of the foldout header + public static IDrawer FoldoutGroup(string title, TEnum mask, ExpandedState state, FoldoutOption options, params ActionDrawer[] contentDrawers) + where TEnum : struct, IConvertible + { + return FoldoutGroup(CoreEditorUtils.GetContent(title), mask, state, options, contentDrawers); + } + + /// + /// Create an IDrawer foldout header using an ExpandedState. + /// The default option is Indent in this version. + /// + /// Title wanted for this foldout header + /// Bit mask (enum) used to define the boolean saving the state in ExpandedState + /// The ExpandedState describing the component + /// The content of the foldout header + public static IDrawer FoldoutGroup(GUIContent title, TEnum mask, ExpandedState state, params IDrawer[] contentDrawers) + where TEnum : struct, IConvertible + { + return FoldoutGroup(title, mask, state, contentDrawers.Draw); + } + + /// + /// Create an IDrawer foldout header using an ExpandedState. + /// The default option is Indent in this version. + /// + /// Title wanted for this foldout header + /// Bit mask (enum) used to define the boolean saving the state in ExpandedState + /// The ExpandedState describing the component + /// The content of the foldout header + public static IDrawer FoldoutGroup(GUIContent title, TEnum mask, ExpandedState state, params ActionDrawer[] contentDrawers) + where TEnum : struct, IConvertible + { + return FoldoutGroup(title, mask, state, FoldoutOption.Indent, contentDrawers); + } + + /// Create an IDrawer foldout header using an ExpandedState + /// Title wanted for this foldout header + /// Bit mask (enum) used to define the boolean saving the state in ExpandedState + /// The ExpandedState describing the component + /// The content of the foldout header + public static IDrawer FoldoutGroup(GUIContent title, TEnum mask, ExpandedState state, FoldoutOption options, params IDrawer[] contentDrawers) + where TEnum : struct, IConvertible + { + return FoldoutGroup(title, mask, state, options, contentDrawers.Draw); + } + + /// Create an IDrawer foldout header using an ExpandedState + /// Title wanted for this foldout header + /// Bit mask (enum) used to define the boolean saving the state in ExpandedState + /// The ExpandedState describing the component + /// The content of the foldout header + public static IDrawer FoldoutGroup(GUIContent title, TEnum mask, ExpandedState state, FoldoutOption options, params ActionDrawer[] contentDrawers) + where TEnum : struct, IConvertible + { + return FoldoutGroup(title, mask, state, options, null, null, contentDrawers); + } + + // This one is private as we do not want to have unhandled advanced switch. Change it if necessary. + static IDrawer FoldoutGroup(GUIContent title, TEnum mask, ExpandedState state, FoldoutOption options, Enabler isAdvanced, SwitchEnabler switchAdvanced, params ActionDrawer[] contentDrawers) + where TEnum : struct, IConvertible + { + return Group((data, owner) => + { + bool isBoxed = (options & FoldoutOption.Boxed) != 0; + bool isIndented = (options & FoldoutOption.Indent) != 0; + bool isSubFoldout = (options & FoldoutOption.SubFoldout) != 0; + bool noSpaceAtEnd = (options & FoldoutOption.NoSpaceAtEnd) != 0; + bool expended = state[mask]; + bool newExpended = expended; + if (isSubFoldout) + { + newExpended = CoreEditorUtils.DrawSubHeaderFoldout(title, expended, isBoxed, + isAdvanced == null ? (Func)null : () => isAdvanced(data, owner), + switchAdvanced == null ? (Action)null : () => switchAdvanced(data, owner)); + } + else + { + CoreEditorUtils.DrawSplitter(); + newExpended = CoreEditorUtils.DrawHeaderFoldout(title, expended, isBoxed, + isAdvanced == null ? (Func)null : () => isAdvanced(data, owner), + switchAdvanced == null ? (Action)null : () => switchAdvanced(data, owner)); + } + if (newExpended ^ expended) + state[mask] = newExpended; + if (newExpended) + { + if (isIndented) + ++EditorGUI.indentLevel; + for (var i = 0; i < contentDrawers.Length; i++) + contentDrawers[i](data, owner); + if (isIndented) + --EditorGUI.indentLevel; + if (!noSpaceAtEnd) + EditorGUILayout.Space(); + } + }); + } + + /// Helper to draw a foldout with an advanced switch on it. + /// Title wanted for this foldout header + /// Bit mask (enum) used to define the boolean saving the state in ExpandedState + /// The ExpandedState describing the component + /// Delegate allowing to check if advanced mode is active. + /// Delegate to know what to do when advance is switched. + /// The content of the foldout header always visible if expended. + /// The content of the foldout header only visible if advanced mode is active and if foldout is expended. + public static IDrawer AdvancedFoldoutGroup(GUIContent foldoutTitle, TEnum foldoutMask, ExpandedState foldoutState, Enabler isAdvanced, SwitchEnabler switchAdvanced, IDrawer normalContent, IDrawer advancedContent, FoldoutOption options = FoldoutOption.Indent) + where TEnum : struct, IConvertible + { + return AdvancedFoldoutGroup(foldoutTitle, foldoutMask, foldoutState, isAdvanced, switchAdvanced, normalContent.Draw, advancedContent.Draw, options); + } + + /// Helper to draw a foldout with an advanced switch on it. + /// Title wanted for this foldout header + /// Bit mask (enum) used to define the boolean saving the state in ExpandedState + /// The ExpandedState describing the component + /// Delegate allowing to check if advanced mode is active. + /// Delegate to know what to do when advance is switched. + /// The content of the foldout header always visible if expended. + /// The content of the foldout header only visible if advanced mode is active and if foldout is expended. + public static IDrawer AdvancedFoldoutGroup(GUIContent foldoutTitle, TEnum foldoutMask, ExpandedState foldoutState, Enabler isAdvanced, SwitchEnabler switchAdvanced, ActionDrawer normalContent, IDrawer advancedContent, FoldoutOption options = FoldoutOption.Indent) + where TEnum : struct, IConvertible + { + return AdvancedFoldoutGroup(foldoutTitle, foldoutMask, foldoutState, isAdvanced, switchAdvanced, normalContent, advancedContent.Draw, options); + } + + /// Helper to draw a foldout with an advanced switch on it. + /// Title wanted for this foldout header + /// Bit mask (enum) used to define the boolean saving the state in ExpandedState + /// The ExpandedState describing the component + /// Delegate allowing to check if advanced mode is active. + /// Delegate to know what to do when advance is switched. + /// The content of the foldout header always visible if expended. + /// The content of the foldout header only visible if advanced mode is active and if foldout is expended. + public static IDrawer AdvancedFoldoutGroup(GUIContent foldoutTitle, TEnum foldoutMask, ExpandedState foldoutState, Enabler isAdvanced, SwitchEnabler switchAdvanced, IDrawer normalContent, ActionDrawer advancedContent, FoldoutOption options = FoldoutOption.Indent) + where TEnum : struct, IConvertible + { + return AdvancedFoldoutGroup(foldoutTitle, foldoutMask, foldoutState, isAdvanced, switchAdvanced, normalContent.Draw, advancedContent, options); + } + + /// Helper to draw a foldout with an advanced switch on it. + /// Title wanted for this foldout header + /// Bit mask (enum) used to define the boolean saving the state in ExpandedState + /// The ExpandedState describing the component + /// Delegate allowing to check if advanced mode is active. + /// Delegate to know what to do when advance is switched. + /// The content of the foldout header always visible if expended. + /// The content of the foldout header only visible if advanced mode is active and if foldout is expended. + public static IDrawer AdvancedFoldoutGroup(GUIContent foldoutTitle, TEnum foldoutMask, ExpandedState foldoutState, Enabler isAdvanced, SwitchEnabler switchAdvanced, ActionDrawer normalContent, ActionDrawer advancedContent, FoldoutOption options = FoldoutOption.Indent) + where TEnum : struct, IConvertible + { + return FoldoutGroup(foldoutTitle, foldoutMask, foldoutState, options, isAdvanced, switchAdvanced, + normalContent, + Conditional((serialized, owner) => isAdvanced(serialized, owner) && foldoutState[foldoutMask], advancedContent).Draw + ); + } } public static class CoreEditorDrawersExtensions { + [Obsolete] public static void Draw(this IEnumerable.IDrawer> drawers, TUIState s, TData p, Editor o) { foreach (var drawer in drawers) drawer.Draw(s, p, o); } + + /// Concatenate a collection of IDrawer as a unique IDrawer + public static void Draw(this IEnumerable.IDrawer> drawers, TData data, Editor owner) + { + foreach (var drawer in drawers) + drawer.Draw(data, owner); + } } } diff --git a/Editor/CoreEditorUtils.cs b/Editor/CoreEditorUtils.cs index 90f3638..d875326 100644 --- a/Editor/CoreEditorUtils.cs +++ b/Editor/CoreEditorUtils.cs @@ -15,7 +15,7 @@ public static class CoreEditorUtils { // GUIContent cache utilities static Dictionary s_GUIContentCache = new Dictionary(); - + public static GUIContent GetContent(string textAndTooltip) { if (string.IsNullOrEmpty(textAndTooltip)) @@ -38,6 +38,11 @@ public static GUIContent GetContent(string textAndTooltip) } // Serialization helpers + /// + /// To use with extreme caution. It not really get the property but try to find a field with similar name + /// Hence inheritance override of property is not supported. + /// Also variable rename will silently break the search. + /// public static string FindProperty(Expression> expr) { // Get the field path as a string @@ -74,11 +79,15 @@ public static GUIContent GetContent(string textAndTooltip) } // UI Helpers - public static void DrawMultipleFields(string label, SerializedProperty[] ppts, GUIContent[] lbls) + { + DrawMultipleFields(GetContent(label), ppts, lbls); + } + + public static void DrawMultipleFields(GUIContent label, SerializedProperty[] ppts, GUIContent[] lbls) { GUILayout.BeginHorizontal(); - EditorGUILayout.PrefixLabel(GetContent(label)); + EditorGUILayout.PrefixLabel(label); GUILayout.BeginVertical(); var labelWidth = EditorGUIUtility.labelWidth; EditorGUIUtility.labelWidth = 45; @@ -112,6 +121,11 @@ public static void DrawSplitter(bool isBoxed = false) } public static void DrawHeader(string title) + { + DrawHeader(GetContent(title)); + } + + public static void DrawHeader(GUIContent title) { var backgroundRect = GUILayoutUtility.GetRect(1f, 17f); @@ -136,9 +150,27 @@ public static void DrawHeader(string title) EditorGUI.LabelField(labelRect, title, EditorStyles.boldLabel); } - public static bool DrawHeaderFoldout(string title, bool state, bool isBoxed = false) + /// Draw a foldout header + /// The title of the header + /// The state of the header + /// [optional] is the eader contained in a box style ? + /// [optional] Delegate used to draw the right state of the advanced button. If null, no button drawn. + /// [optional] Callback call when advanced button clicked. Should be used to toggle its state. + public static bool DrawHeaderFoldout(string title, bool state, bool isBoxed = false, Func isAdvanced = null, Action switchAdvanced = null) { - var backgroundRect = GUILayoutUtility.GetRect(1f, 17f); + return DrawHeaderFoldout(GetContent(title), state, isBoxed, isAdvanced, switchAdvanced); + } + + /// Draw a foldout header + /// The title of the header + /// The state of the header + /// [optional] is the eader contained in a box style ? + /// [optional] Delegate used to draw the right state of the advanced button. If null, no button drawn. + /// [optional] Callback call when advanced button clicked. Should be used to toggle its state. + public static bool DrawHeaderFoldout(GUIContent title, bool state, bool isBoxed = false, Func isAdvanced = null, Action switchAdvanced = null) + { + const float height = 17f; + var backgroundRect = GUILayoutUtility.GetRect(1f, height); var labelRect = backgroundRect; labelRect.xMin += 16f; @@ -148,6 +180,34 @@ public static bool DrawHeaderFoldout(string title, bool state, bool isBoxed = fa foldoutRect.y += 1f; foldoutRect.width = 13f; foldoutRect.height = 13f; + + var advancedRect = new Rect(); + if (isAdvanced != null) + { + advancedRect = backgroundRect; + advancedRect.x += advancedRect.width - 16 - 1; + advancedRect.y -= 2; + advancedRect.height = 16; + advancedRect.width = 16; + + GUIStyle styleAdvanced = new GUIStyle(GUI.skin.toggle); + styleAdvanced.normal.background = isAdvanced() + ? Resources.Load("Advanced_Pressed_mini") + : Resources.Load("Advanced_UnPressed_mini"); + styleAdvanced.onActive.background = styleAdvanced.normal.background; + styleAdvanced.onFocused.background = styleAdvanced.normal.background; + styleAdvanced.onNormal.background = styleAdvanced.normal.background; + styleAdvanced.onHover.background = styleAdvanced.normal.background; + styleAdvanced.active.background = styleAdvanced.normal.background; + styleAdvanced.focused.background = styleAdvanced.normal.background; + styleAdvanced.hover.background = styleAdvanced.normal.background; + EditorGUI.BeginChangeCheck(); + GUI.Toggle(advancedRect, isAdvanced(), GUIContent.none, styleAdvanced); + if(EditorGUI.EndChangeCheck() && switchAdvanced != null) + { + switchAdvanced(); + } + } // Background rect should be full-width backgroundRect.xMin = 0f; @@ -172,7 +232,95 @@ public static bool DrawHeaderFoldout(string title, bool state, bool isBoxed = fa state = GUI.Toggle(foldoutRect, state, GUIContent.none, EditorStyles.foldout); var e = Event.current; - if (e.type == EventType.MouseDown && backgroundRect.Contains(e.mousePosition) && e.button == 0) + if (e.type == EventType.MouseDown && backgroundRect.Contains(e.mousePosition) && !advancedRect.Contains(e.mousePosition) && e.button == 0) + { + state = !state; + e.Use(); + } + + return state; + } + + /// Draw a foldout header + /// The title of the header + /// The state of the header + /// [optional] is the eader contained in a box style ? + /// [optional] Delegate used to draw the right state of the advanced button. If null, no button drawn. + /// [optional] Callback call when advanced button clicked. Should be used to toggle its state. + public static bool DrawSubHeaderFoldout(string title, bool state, bool isBoxed = false, Func isAdvanced = null, Action switchAdvanced = null) + { + return DrawSubHeaderFoldout(GetContent(title), state, isBoxed, isAdvanced, switchAdvanced); + } + + /// Draw a foldout header + /// The title of the header + /// The state of the header + /// [optional] is the eader contained in a box style ? + /// [optional] Delegate used to draw the right state of the advanced button. If null, no button drawn. + /// [optional] Callback call when advanced button clicked. Should be used to toggle its state. + public static bool DrawSubHeaderFoldout(GUIContent title, bool state, bool isBoxed = false, Func isAdvanced = null, Action switchAdvanced = null) + { + const float height = 17f; + var backgroundRect = GUILayoutUtility.GetRect(1f, height); + + var labelRect = backgroundRect; + labelRect.xMin += 16f; + labelRect.xMax -= 20f; + + var foldoutRect = backgroundRect; + foldoutRect.y += 1f; + foldoutRect.x += 15 * EditorGUI.indentLevel; //GUI do not handle indent. Handle it here + foldoutRect.width = 13f; + foldoutRect.height = 13f; + + var advancedRect = new Rect(); + if (isAdvanced != null) + { + advancedRect = backgroundRect; + advancedRect.x += advancedRect.width - 16 - 1; + advancedRect.y -= 2; + advancedRect.height = 16; + advancedRect.width = 16; + + GUIStyle styleAdvanced = new GUIStyle(GUI.skin.toggle); + styleAdvanced.normal.background = isAdvanced() + ? Resources.Load("Advanced_Pressed_mini") + : Resources.Load("Advanced_UnPressed_mini"); + styleAdvanced.onActive.background = styleAdvanced.normal.background; + styleAdvanced.onFocused.background = styleAdvanced.normal.background; + styleAdvanced.onNormal.background = styleAdvanced.normal.background; + styleAdvanced.onHover.background = styleAdvanced.normal.background; + styleAdvanced.active.background = styleAdvanced.normal.background; + styleAdvanced.focused.background = styleAdvanced.normal.background; + styleAdvanced.hover.background = styleAdvanced.normal.background; + EditorGUI.BeginChangeCheck(); + GUI.Toggle(advancedRect, isAdvanced(), GUIContent.none, styleAdvanced); + if (EditorGUI.EndChangeCheck() && switchAdvanced != null) + { + switchAdvanced(); + } + } + + // Background rect should be full-width + backgroundRect.xMin = 0f; + backgroundRect.width += 4f; + + if (isBoxed) + { + labelRect.xMin += 5; + foldoutRect.xMin += 5; + backgroundRect.xMin = EditorGUIUtility.singleLineHeight; + backgroundRect.width -= 3; + } + + // Title + EditorGUI.LabelField(labelRect, title, EditorStyles.boldLabel); + + // Active checkbox + state = GUI.Toggle(foldoutRect, state, GUIContent.none, EditorStyles.foldout); + + var e = Event.current; + if (e.type == EventType.MouseDown && backgroundRect.Contains(e.mousePosition) && !advancedRect.Contains(e.mousePosition) && e.button == 0) { state = !state; e.Use(); @@ -182,6 +330,11 @@ public static bool DrawHeaderFoldout(string title, bool state, bool isBoxed = fa } public static bool DrawHeaderToggle(string title, SerializedProperty group, SerializedProperty activeField, Action contextAction = null) + { + return DrawHeaderToggle(GetContent(title), group, activeField, contextAction); + } + + public static bool DrawHeaderToggle(GUIContent title, SerializedProperty group, SerializedProperty activeField, Action contextAction = null) { var backgroundRect = GUILayoutUtility.GetRect(1f, 17f); @@ -210,7 +363,7 @@ public static bool DrawHeaderToggle(string title, SerializedProperty group, Seri // Title using (new EditorGUI.DisabledScope(!activeField.boolValue)) - EditorGUI.LabelField(labelRect, GetContent(title), EditorStyles.boldLabel); + EditorGUI.LabelField(labelRect, title, EditorStyles.boldLabel); // Foldout group.serializedObject.Update(); diff --git a/Editor/ExpandedState.cs b/Editor/ExpandedState.cs new file mode 100644 index 0000000..7b7ad7b --- /dev/null +++ b/Editor/ExpandedState.cs @@ -0,0 +1,68 @@ +using System; +using UnityEditor; + +namespace UnityEditor.Experimental.Rendering +{ + /// Used in editor drawer part to store the state of expendable areas. + /// An enum to use to describe the state. + /// A type given to automatically compute the key. + public struct ExpandedState + where TState : struct, IConvertible + { + /// Key is automatically computed regarding the target type given + public readonly string stateKey; + + /// Constructor will create the key to store in the EditorPref the state given generic type passed. + /// If key did not exist, it will be created with this value for initialization. + public ExpandedState(TState defaultValue, string prefix = "CoreRP") + { + stateKey = string.Format("{0}:{1}:UI_State", prefix, typeof(TTarget).Name); + + //register key if not already there + if (!EditorPrefs.HasKey(stateKey)) + { + EditorPrefs.SetInt(stateKey, (int)(object)defaultValue); + } + } + + uint expandedState { get { return (uint)EditorPrefs.GetInt(stateKey); } set { EditorPrefs.SetInt(stateKey, (int)value); } } + + /// Get or set the state given the mask. + public bool this[TState mask] + { + get { return GetExpandedAreas(mask); } + set { SetExpandedAreas(mask, value); } + } + + /// Accessor to the expended state of this specific mask. + public bool GetExpandedAreas(TState mask) + { + // note on cast: + // - to object always ok + // - to int ok because of IConvertible. Cannot directly go to uint + return (expandedState & (uint)(int)(object)mask) > 0; + } + + /// Setter to the expended state. + public void SetExpandedAreas(TState mask, bool value) + { + uint state = expandedState; + // note on cast: + // - to object always ok + // - to int ok because of IConvertible. Cannot directly go to uint + uint workMask = (uint)(int)(object)mask; + + if (value) + { + state |= workMask; + } + else + { + workMask = ~workMask; + state &= workMask; + } + + expandedState = state; + } + } +} diff --git a/Editor/ExpandedState.cs.meta b/Editor/ExpandedState.cs.meta new file mode 100644 index 0000000..ef6cc42 --- /dev/null +++ b/Editor/ExpandedState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1f4f20df9923ed04ea15d89f8456b8f0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/PropertyFetcher.cs b/Editor/PropertyFetcher.cs index f0368c0..cba6664 100644 --- a/Editor/PropertyFetcher.cs +++ b/Editor/PropertyFetcher.cs @@ -18,7 +18,12 @@ public SerializedProperty Find(string str) { return obj.FindProperty(str); } - + + /// + /// To use with extreme caution. It not really get the property but try to find a field with similar name + /// Hence inheritance override of property is not supported. + /// Also variable rename will silently break the search. + /// public SerializedProperty Find(Expression> expr) { string path = CoreEditorUtils.FindProperty(expr); @@ -46,6 +51,11 @@ public SerializedProperty Find(string str) return obj.FindPropertyRelative(str); } + /// + /// To use with extreme caution. It not really get the property but try to find a field with similar name + /// Hence inheritance override of property is not supported. + /// Also variable rename will silently break the search. + /// public SerializedProperty Find(Expression> expr) { string path = CoreEditorUtils.FindProperty(expr); @@ -60,12 +70,22 @@ public void Dispose() public static class PropertyFetcherExtensions { + /// + /// To use with extreme caution. It not really get the property but try to find a field with similar name + /// Hence inheritance override of property is not supported. + /// Also variable rename will silently break the search. + /// public static SerializedProperty Find(this SerializedObject obj, Expression> expr) { var path = CoreEditorUtils.FindProperty(expr); return obj.FindProperty(path); } + /// + /// To use with extreme caution. It not really get the property but try to find a field with similar name + /// Hence inheritance override of property is not supported. + /// Also variable rename will silently break the search. + /// public static SerializedProperty Find(this SerializedProperty obj, Expression> expr) { var path = CoreEditorUtils.FindProperty(expr); diff --git a/Editor/Resources.meta b/Editor/Resources.meta new file mode 100644 index 0000000..1b74c55 --- /dev/null +++ b/Editor/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e528aa751bf6fe44398a1f70eaac287c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Resources/Advanced_Pressed_mini.png b/Editor/Resources/Advanced_Pressed_mini.png new file mode 100644 index 0000000..4f484cd Binary files /dev/null and b/Editor/Resources/Advanced_Pressed_mini.png differ diff --git a/Editor/Resources/Advanced_Pressed_mini.png.meta b/Editor/Resources/Advanced_Pressed_mini.png.meta new file mode 100644 index 0000000..55904ab --- /dev/null +++ b/Editor/Resources/Advanced_Pressed_mini.png.meta @@ -0,0 +1,121 @@ +fileFormatVersion: 2 +guid: 3e471b9fad7264146aa03aca58b27957 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 7 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 12 + mipBias: -100 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - serializedVersion: 2 + buildTarget: DefaultTexturePlatform + maxTextureSize: 32 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + - serializedVersion: 2 + buildTarget: Standalone + maxTextureSize: 32 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + - serializedVersion: 2 + buildTarget: Windows Store Apps + maxTextureSize: 32 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + - serializedVersion: 2 + buildTarget: iPhone + maxTextureSize: 32 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Resources/Advanced_UnPressed_mini.png b/Editor/Resources/Advanced_UnPressed_mini.png new file mode 100644 index 0000000..056aa9a Binary files /dev/null and b/Editor/Resources/Advanced_UnPressed_mini.png differ diff --git a/Editor/Resources/Advanced_UnPressed_mini.png.meta b/Editor/Resources/Advanced_UnPressed_mini.png.meta new file mode 100644 index 0000000..8d2505c --- /dev/null +++ b/Editor/Resources/Advanced_UnPressed_mini.png.meta @@ -0,0 +1,121 @@ +fileFormatVersion: 2 +guid: ad4dcac393f63f3468f6d9ff1652732e +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 7 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 12 + mipBias: -100 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - serializedVersion: 2 + buildTarget: DefaultTexturePlatform + maxTextureSize: 32 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + - serializedVersion: 2 + buildTarget: Standalone + maxTextureSize: 32 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + - serializedVersion: 2 + buildTarget: Windows Store Apps + maxTextureSize: 32 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + - serializedVersion: 2 + buildTarget: iPhone + maxTextureSize: 32 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Utilities/CoreUnsafeUtils.cs b/Runtime/Utilities/CoreUnsafeUtils.cs new file mode 100644 index 0000000..0422285 --- /dev/null +++ b/Runtime/Utilities/CoreUnsafeUtils.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using Unity.Collections.LowLevel.Unsafe; + +namespace UnityEngine.Experimental.Rendering +{ + public static unsafe class CoreUnsafeUtils + { + public static void CopyTo(this List list, void* dest, int count) + where T : struct + { + var c = Mathf.Min(count, list.Count); + for (int i = 0; i < c; ++i) + UnsafeUtility.WriteArrayElement(dest, i, list[i]); + } + + public static void CopyTo(this T[] list, void* dest, int count) + where T : struct + { + var c = Mathf.Min(count, list.Length); + for (int i = 0; i < c; ++i) + UnsafeUtility.WriteArrayElement(dest, i, list[i]); + } + + public static void QuickSort(int count, void* data) + where T : struct, IComparable + { + QuickSort(data, 0, count - 1); + } + + public static unsafe void QuickSort(uint[] arr, int left, int right) + { + fixed (uint* ptr = arr) + QuickSort(ptr, left, right); + } + + public static void QuickSort(void* data, int left, int right) + where T : struct, IComparable + { + // For Recursion + if (left < right) + { + int pivot = Partition(data, left, right); + + if (pivot > 1) + QuickSort(data, left, pivot); + + if (pivot + 1 < right) + QuickSort(data, pivot + 1, right); + } + } + + // Just a sort function that doesn't allocate memory + // Note: Should be replace by a radix sort for positive integer + static int Partition(void* data, int left, int right) + where T : struct, IComparable + { + var pivot = UnsafeUtility.ReadArrayElement(data, left); + + --left; + ++right; + while (true) + { + var lvalue = default(T); + do { ++left; } + while ((lvalue = UnsafeUtility.ReadArrayElement(data, left)).CompareTo(pivot) < 0); + + var rvalue = default(T); + do { --right; } + while ((rvalue = UnsafeUtility.ReadArrayElement(data, right)).CompareTo(pivot) > 0); + + if (left < right) + { + UnsafeUtility.WriteArrayElement(data, right, lvalue); + UnsafeUtility.WriteArrayElement(data, left, rvalue); + } + else + { + return right; + } + } + } + + public static unsafe bool HaveDuplicates(int[] arr) + { + int* copy = stackalloc int[arr.Length]; + arr.CopyTo(copy, arr.Length); + QuickSort(arr.Length, copy); + for (int i = arr.Length - 1; i > 0; --i) + { + if (UnsafeUtility.ReadArrayElement(copy, i).CompareTo(UnsafeUtility.ReadArrayElement(copy, i - 1)) == 0) + { + return true; + } + } + return false; + } + } +} diff --git a/Runtime/Utilities/CoreUnsafeUtils.cs.meta b/Runtime/Utilities/CoreUnsafeUtils.cs.meta new file mode 100644 index 0000000..23cf41c --- /dev/null +++ b/Runtime/Utilities/CoreUnsafeUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 69a7aaed92ae1b0469d9b82fba45bcec +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ShaderLibrary/API/D3D11.hlsl b/ShaderLibrary/API/D3D11.hlsl index 4f9c8c4..9c0eb31 100644 --- a/ShaderLibrary/API/D3D11.hlsl +++ b/ShaderLibrary/API/D3D11.hlsl @@ -14,6 +14,9 @@ #define CBUFFER_START(name) cbuffer name { #define CBUFFER_END }; +#define PLATFORM_SUPPORTS_EXPLICIT_BINDING 1 +#define PLATFORM_NEEDS_UNORM_UAV_SPECIFIER 1 + // flow control attributes #define UNITY_BRANCH [branch] #define UNITY_FLATTEN [flatten] diff --git a/ShaderLibrary/API/GLCore.hlsl b/ShaderLibrary/API/GLCore.hlsl index e44ca97..a913188 100644 --- a/ShaderLibrary/API/GLCore.hlsl +++ b/ShaderLibrary/API/GLCore.hlsl @@ -20,6 +20,8 @@ #define CBUFFER_START(name) #define CBUFFER_END +#define PLATFORM_SUPPORTS_EXPLICIT_BINDING 1 + // flow control attributes #define UNITY_BRANCH [branch] #define UNITY_FLATTEN [flatten] diff --git a/ShaderLibrary/API/Metal.hlsl b/ShaderLibrary/API/Metal.hlsl index f220fbc..51c53bc 100644 --- a/ShaderLibrary/API/Metal.hlsl +++ b/ShaderLibrary/API/Metal.hlsl @@ -15,6 +15,8 @@ #define CBUFFER_START(name) cbuffer name { #define CBUFFER_END }; +#define PLATFORM_SUPPORTS_EXPLICIT_BINDING 1 + // flow control attributes #define UNITY_BRANCH [branch] #define UNITY_FLATTEN [flatten] diff --git a/ShaderLibrary/API/PSSL.hlsl b/ShaderLibrary/API/PSSL.hlsl index 09c351c..5bf19e8 100644 --- a/ShaderLibrary/API/PSSL.hlsl +++ b/ShaderLibrary/API/PSSL.hlsl @@ -9,29 +9,34 @@ #define INTRINSIC_BITFIELD_INSERT #define BitFieldInsert __v_bfi_b32 #define INTRINSIC_WAVEREADFIRSTLANE -#define WaveReadFirstLane ReadFirstLane +#define WaveReadLaneFirst ReadFirstLane #define INTRINSIC_MAD24 -#define Mad24Int mad24 -#define Mad24Uint mad24 +#define Mad24 mad24 #define INTRINSIC_MINMAX3 #define Min3 min3 #define Max3 max3 #define INTRINSIC_CUBEMAP_FACE_ID #define INTRINSIC_WAVE_MINMAX -#define WaveMinInt CrossLaneMin -#define WaveMinUint CrossLaneMin -#define WaveMinFloat CrossLaneMin -#define WaveMaxInt CrossLaneMax -#define WaveMaxUint CrossLaneMax -#define WaveMaxFloat CrossLaneMax +#define WaveActiveMin CrossLaneMin +#define WaveActiveMax CrossLaneMax #define INTRINSIC_BALLOT -#define Ballot ballot +#define WaveActiveBallot ballot #define INTRINSIC_WAVE_SUM -#define WaveAdd CrossLaneAdd +#define WaveActiveSum CrossLaneAdd #define INTRINSIC_WAVE_LOGICAL_OPS -#define WaveAnd CrossLaneAnd -#define WaveOr CrossLaneOr +#define WaveActiveBitAnd CrossLaneAnd +#define WaveActiveBitOr CrossLaneOr +#define INTRINSIC_WAVE_ACTIVE_ALL_ANY +bool WaveActiveAllTrue(bool expression) +{ + return (__s_read_exec() == WaveActiveBallot(expression)); +} + +bool WaveActiveAnyTrue(bool expression) +{ + return (popcnt(WaveActiveBallot(expression))) != 0; +} #define UNITY_UV_STARTS_AT_TOP 1 @@ -160,4 +165,4 @@ #define GATHER_RED_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherRed(samplerName, coord2) #define GATHER_GREEN_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherGreen(samplerName, coord2) #define GATHER_BLUE_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherBlue(samplerName, coord2) -#define GATHER_ALPHA_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherAlpha(samplerName, coord2) \ No newline at end of file +#define GATHER_ALPHA_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherAlpha(samplerName, coord2) diff --git a/ShaderLibrary/API/Vulkan.hlsl b/ShaderLibrary/API/Vulkan.hlsl index f3c203a..8ae558b 100644 --- a/ShaderLibrary/API/Vulkan.hlsl +++ b/ShaderLibrary/API/Vulkan.hlsl @@ -15,6 +15,8 @@ #define CBUFFER_START(name) cbuffer name { #define CBUFFER_END }; +#define PLATFORM_SUPPORTS_EXPLICIT_BINDING 1 + // flow control attributes #define UNITY_BRANCH [branch] #define UNITY_FLATTEN [flatten] diff --git a/ShaderLibrary/API/XBoxOne.hlsl b/ShaderLibrary/API/XBoxOne.hlsl index 3b20f6c..7c150a6 100644 --- a/ShaderLibrary/API/XBoxOne.hlsl +++ b/ShaderLibrary/API/XBoxOne.hlsl @@ -1,5 +1,20 @@ // This file assume SHADER_API_XBOXONE is defined +#define GENERATE_INTRINSIC_VARIANTS_1_ARG(FunctionName, BaseIntrinsicName, Parameter0) \ + float FunctionName(float Parameter0) { return BaseIntrinsicName##F32(Parameter0); } \ + int FunctionName(int Parameter0) { return BaseIntrinsicName##I32(Parameter0); } \ + uint FunctionName(uint Parameter0) { return BaseIntrinsicName##U32(Parameter0); } + +#define GENERATE_INTRINSIC_VARIANTS_3_ARGS(FunctionName, BaseIntrinsicName, Parameter0, Parameter1, Parameter2) \ + float FunctionName(float Parameter0, float Parameter1, float Parameter2) { return BaseIntrinsicName##F32(Parameter0, Parameter1, Parameter2); } \ + int FunctionName(int Parameter0, int Parameter1, int Parameter2) { return BaseIntrinsicName##I32(Parameter0, Parameter1, Parameter2); } \ + uint FunctionName(uint Parameter0, uint Parameter1, uint Parameter2) { return BaseIntrinsicName##U32(Parameter0, Parameter1, Parameter2); } + +#define GENERATE_INTRINSIC_INT24_VARIANTS_3_ARGS(FunctionName, BaseIntrinsicName, Parameter0, Parameter1, Parameter2) \ + int FunctionName(int Parameter0, int Parameter1, int Parameter2) { return BaseIntrinsicName##I24(Parameter0, Parameter1, Parameter2); } \ + uint FunctionName(uint Parameter0, uint Parameter1, uint Parameter2) { return BaseIntrinsicName##U24(Parameter0, Parameter1, Parameter2); } + + #define UNITY_UV_STARTS_AT_TOP 1 #define UNITY_REVERSED_Z 1 #define UNITY_NEAR_CLIP_VALUE (1.0) @@ -14,37 +29,54 @@ #define CBUFFER_START(name) cbuffer name { #define CBUFFER_END }; +#define PLATFORM_SUPPORTS_EXPLICIT_BINDING 1 +#define PLATFORM_NEEDS_UNORM_UAV_SPECIFIER 1 + // Intrinsics #define SUPPORTS_WAVE_INTRINSICS #define INTRINSIC_WAVEREADFIRSTLANE -#define WaveReadFirstLane __XB_MakeUniform -#define INTRINSIC_MINMAX3 -#define Min3 __XB_Min3_F32 -#define Max3 __XB_Max3_F32 -#define INTRINSIC_MAD24 -#define Mad24Int __XB_MadI24 -#define Mad24Uint __XB_MadU24 +#define WaveReadLaneFirst __XB_MakeUniform #define INTRINSIC_BITFIELD_EXTRACT #define BitFieldExtract __XB_UBFE #define INTRINSIC_BITFIELD_EXTRACT_SIGN_EXTEND #define BitFieldExtractSignExtend __XB_IBFE #define INTRINSIC_BITFIELD_INSERT #define BitFieldInsert __XB_BFI -#define INTRINSIC_WAVE_MINMAX -#define WaveMinInt __XB_WaveMin_I32 -#define WaveMinUint __XB_WaveMin_U32 -#define WaveMinFloat __XB_WaveMin_F32 -#define WaveMaxInt __XB_WaveMax_I32 -#define WaveMaxUint __XB_WaveMax_U32 -#define WaveMaxFloat __XB_WaveMax_F32 #define INTRINSIC_BALLOT -#define Ballot __XB_Ballot64 -#define INTRINSIC_WAVE_SUM -#define WaveAdd __XB_WaveAdd_F32 +#define WaveActiveBallot __XB_Ballot64 #define INTRINSIC_WAVE_LOGICAL_OPS -#define WaveAnd __XB_WaveAND -#define WaveOr __XB_WaveOR +#define WaveActiveBitAnd __XB_WaveAND +#define WaveActiveBitOr __XB_WaveOR + +#define INTRINSIC_WAVE_ACTIVE_ALL_ANY +bool WaveActiveAllTrue(bool expression) +{ + return all(WaveActiveBallot(true) == WaveActiveBallot(expression)); +} + +bool WaveActiveAnyTrue(bool expression) +{ + return (__XB_S_BCNT1_U64(WaveActiveBallot(expression))) != 0; +} + + +#define INTRINSIC_MINMAX3 +GENERATE_INTRINSIC_VARIANTS_3_ARGS(Min3, __XB_Min3_, a, b, c); +GENERATE_INTRINSIC_VARIANTS_3_ARGS(Max3, __XB_Max3_, a, b, c); + +#define INTRINSIC_WAVE_MINMAX +GENERATE_INTRINSIC_VARIANTS_1_ARG(WaveActiveMin, __XB_WaveMin_, value); +GENERATE_INTRINSIC_VARIANTS_1_ARG(WaveActiveMax, __XB_WaveMax_, value); + +#define INTRINSIC_MAD24 +GENERATE_INTRINSIC_INT24_VARIANTS_3_ARGS(Mad24, __XB_Mad, a, b, c); + +#define INTRINSIC_WAVE_SUM +float WaveActiveSum(float value) +{ + return __XB_WaveAdd_F32(value); +} // flow control attributes #define UNITY_BRANCH [branch] @@ -158,4 +190,4 @@ #define GATHER_RED_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherRed(samplerName, coord2) #define GATHER_GREEN_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherGreen(samplerName, coord2) #define GATHER_BLUE_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherBlue(samplerName, coord2) -#define GATHER_ALPHA_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherAlpha(samplerName, coord2) \ No newline at end of file +#define GATHER_ALPHA_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherAlpha(samplerName, coord2) diff --git a/ShaderLibrary/Common.hlsl b/ShaderLibrary/Common.hlsl index 79f93ef..aaa1135 100644 --- a/ShaderLibrary/Common.hlsl +++ b/ShaderLibrary/Common.hlsl @@ -182,16 +182,14 @@ // On everything but GCN consoles we error on cross-lane operations #ifndef SUPPORTS_WAVE_INTRINSICS -#define WaveMinInt ERROR_ON_UNSUPPORTED_FUNC(WaveMinInt) -#define WaveMinUint ERROR_ON_UNSUPPORTED_FUNC(WaveMinUint) -#define WaveMinFloat ERROR_ON_UNSUPPORTED_FUNC(WaveMinFloat) -#define WaveMaxInt ERROR_ON_UNSUPPORTED_FUNC(WaveMaxInt) -#define WaveMaxUint ERROR_ON_UNSUPPORTED_FUNC(WaveMaxUint) -#define WaveMaxFloat ERROR_ON_UNSUPPORTED_FUNC(WaveMaxFloat) -#define Ballot ERROR_ON_UNSUPPORTED_FUNC(Ballot) -#define WaveAdd ERROR_ON_UNSUPPORTED_FUNC(WaveAdd) -#define WaveAnd ERROR_ON_UNSUPPORTED_FUNC(WaveAnd) -#define WaveOr ERROR_ON_UNSUPPORTED_FUNC(WaveOr) +#define WaveActiveAllTrue ERROR_ON_UNSUPPORTED_FUNC(WaveActiveAllTrue) +#define WaveActiveAnyTrue ERROR_ON_UNSUPPORTED_FUNC(WaveActiveAnyTrue) +#define WaveActiveMin ERROR_ON_UNSUPPORTED_FUNC(WaveActiveMin) +#define WaveActiveMax ERROR_ON_UNSUPPORTED_FUNC(WaveActiveMax) +#define WaveActiveBallot ERROR_ON_UNSUPPORTED_FUNC(WaveActiveBallot) +#define WaveActiveSum ERROR_ON_UNSUPPORTED_FUNC(WaveActiveSum) +#define WaveActiveBitAnd ERROR_ON_UNSUPPORTED_FUNC(WaveActiveBitAnd) +#define WaveActiveBitOr ERROR_ON_UNSUPPORTED_FUNC(WaveActiveBitOr) #endif #if !defined(SHADER_API_GLES) @@ -199,7 +197,7 @@ #ifndef INTRINSIC_BITFIELD_EXTRACT // Unsigned integer bit field extraction. // Note that the intrinsic itself generates a vector instruction. -// Wrap this function with WaveReadFirstLane() to get scalar output. +// Wrap this function with WaveReadLaneFirst() to get scalar output. uint BitFieldExtract(uint data, uint offset, uint numBits) { uint mask = (1u << numBits) - 1u; @@ -210,7 +208,7 @@ uint BitFieldExtract(uint data, uint offset, uint numBits) #ifndef INTRINSIC_BITFIELD_EXTRACT_SIGN_EXTEND // Integer bit field extraction with sign extension. // Note that the intrinsic itself generates a vector instruction. -// Wrap this function with WaveReadFirstLane() to get scalar output. +// Wrap this function with WaveReadLaneFirst() to get scalar output. int BitFieldExtractSignExtend(int data, uint offset, uint numBits) { int shifted = data >> offset; // Sign-extending (arithmetic) shift @@ -252,8 +250,8 @@ void ToggleBit(inout uint data, uint offset) #ifndef INTRINSIC_WAVEREADFIRSTLANE // Warning: for correctness, the argument's value must be the same across all lanes of the wave. - TEMPLATE_1_REAL(WaveReadFirstLane, scalarValue, return scalarValue) - TEMPLATE_1_INT(WaveReadFirstLane, scalarValue, return scalarValue) + TEMPLATE_1_REAL(WaveReadLaneFirst, scalarValue, return scalarValue) + TEMPLATE_1_INT(WaveReadLaneFirst, scalarValue, return scalarValue) #endif #ifndef INTRINSIC_MUL24 @@ -261,8 +259,7 @@ void ToggleBit(inout uint data, uint offset) #endif // INTRINSIC_MUL24 #ifndef INTRINSIC_MAD24 - TEMPLATE_3_INT(Mad24Int, a, b, c, return a * b + c) - TEMPLATE_3_INT(Mad24Uint, a, b, c, return a * b + c) + TEMPLATE_3_INT(Mad24, a, b, c, return a * b + c) #endif // INTRINSIC_MAD24 #ifndef INTRINSIC_MINMAX3 diff --git a/ShaderLibrary/Random.hlsl b/ShaderLibrary/Random.hlsl index 146796c..ade4ef1 100644 --- a/ShaderLibrary/Random.hlsl +++ b/ShaderLibrary/Random.hlsl @@ -90,6 +90,16 @@ float2 InitRandom(float2 input) return r; } +//From Next Generation Post Processing in Call of Duty: Advanced Warfare [Jimenez 2014] +// http://advances.realtimerendering.com/s2014/index.html +float InterleavedGradientNoise(float2 uv, uint frameCount) +{ + const float3 magic = float3(0.06711056f, 0.00583715f, 52.9829189f); + float2 frameMagicScale = float2(2.083f, 4.867f); + uv += frameCount * frameMagicScale; + return frac(magic.z * frac(dot(uv, magic.xy))); +} + #endif // SHADER_API_GLES #endif // UNITY_RANDOM_INCLUDED diff --git a/ShaderLibrary/UnityInstancing.hlsl b/ShaderLibrary/UnityInstancing.hlsl index 5d840cf..b81e1c0 100644 --- a/ShaderLibrary/UnityInstancing.hlsl +++ b/ShaderLibrary/UnityInstancing.hlsl @@ -27,6 +27,7 @@ // - UNITY_INSTANCING_ENABLED Defined if instancing path is taken. // - UNITY_PROCEDURAL_INSTANCING_ENABLED Defined if procedural instancing path is taken. // - UNITY_STEREO_INSTANCING_ENABLED Defined if stereo instancing path is taken. +// - UNITY_ANY_INSTANCING_ENABLED Defined if any instancing path is taken #if defined(UNITY_SUPPORT_INSTANCING) && defined(INSTANCING_ON) #define UNITY_INSTANCING_ENABLED #endif @@ -37,6 +38,12 @@ #define UNITY_STEREO_INSTANCING_ENABLED #endif +#if defined(UNITY_INSTANCING_ENABLED) || defined(UNITY_PROCEDURAL_INSTANCING_ENABLED) || defined(UNITY_STEREO_INSTANCING_ENABLED) + #define UNITY_ANY_INSTANCING_ENABLED 1 +#else + #define UNITY_ANY_INSTANCING_ENABLED 0 +#endif + #if defined(SHADER_API_GLES3) || defined(SHADER_API_GLCORE) || defined(SHADER_API_METAL) || defined(SHADER_API_VULKAN) // These platforms have constant buffers disabled normally, but not here (see CBUFFER_START/CBUFFER_END in HLSLSupport.cginc). #define UNITY_INSTANCING_CBUFFER_SCOPE_BEGIN(name) cbuffer name { diff --git a/ShaderLibrary/VolumeRendering.hlsl b/ShaderLibrary/VolumeRendering.hlsl index e1983e6..c2524f3 100644 --- a/ShaderLibrary/VolumeRendering.hlsl +++ b/ShaderLibrary/VolumeRendering.hlsl @@ -214,11 +214,8 @@ real CornetteShanksPhasePartVarying(real anisotropy, real cosTheta) real f = rsqrt(saturate(1 + g * g - 2 * g * cosTheta)); // x^(-1/2) real h = (1 + cosTheta * cosTheta); - // Note that this function is not perfectly isotropic for (g = 0). We force it to be. - // TODO: in the future, when (g = 0), specialize the Volumetric Lighting kernel - // to not do anything anisotropy-specific. This way we could avoid this test - // (along with tons of other overhead and hacks). - return (g == 0) ? 1.33333333 : h * (f * f * f); // h * x^(-3/2) + // Note that this function is not perfectly isotropic for (g = 0). + return h * (f * f * f); // h * x^(-3/2) } // A better approximation of the Mie phase function. diff --git a/package.json b/package.json index fae880b..87f71dd 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "description": "Helper library for SRP that contains a new Shader Library, and utility functions that can be used to implement a custom SRP. This library is currently used by both the High Definition Render Pipeline and the Lightweight Render Pipeline.", "displayName": "Core RP Library", - "gitHead": "737b5dd3a7a25344fabf75b9eb89f15ac641c95b", + "gitHead": "fc353aa8874574ebf9de4d6d458a44c1c58e40eb", "name": "com.unity.render-pipelines.core", "repoPackagePath": "com.unity.render-pipelines.core", "repository": { @@ -9,5 +9,5 @@ "url": "ssh://git@github.com/Unity-Technologies/ScriptableRenderLoop.git" }, "unity": "2018.3", - "version": "4.1.0-preview" + "version": "4.2.0-preview" } \ No newline at end of file