diff --git a/Documentation~/DocAssets/FoldoutGroupsScreenshot.jpg b/Documentation~/DocAssets/FoldoutGroupsScreenshot.jpg new file mode 100644 index 0000000..b48d75e Binary files /dev/null and b/Documentation~/DocAssets/FoldoutGroupsScreenshot.jpg differ diff --git a/Editor/ShaderGraphEditor/OpenGraphGUIEditor.cs b/Editor/ShaderGraphEditor/OpenGraphGUIEditor.cs index d6c4ff1..fdd4287 100644 --- a/Editor/ShaderGraphEditor/OpenGraphGUIEditor.cs +++ b/Editor/ShaderGraphEditor/OpenGraphGUIEditor.cs @@ -55,6 +55,10 @@ public class OpenGraphGUIEditor : ShaderGUI /// Amount of tab spacing for single line texture property. /// const float singleLineTexTabSpace = 2f; + /// + /// Amount of spacing above label properties. + /// + const float labelTopSpace = 4f; const string centeredSpacingName = "[centered]"; const string rightBoundSpacingName = "[rightbound]"; @@ -62,6 +66,7 @@ public class OpenGraphGUIEditor : ShaderGUI const string minFieldSpacingName = "[minfield]"; const string labelPrefix = "*"; + const string foldoutPrefix = "#"; const string singleLineTexPrefix = "%"; const string dependentVisibleTextPrefix = "^"; const string linkedPropertyPrefix = "&"; @@ -83,11 +88,26 @@ class LinkedProperty private bool fieldCenteredMode = false; private bool fieldExpandedMode = false; + private bool hadOneFoldout = false; + private bool currentlyInFoldout = false; + private int currentFoldoutIndex = 0; + private bool bottomOptionsFoldout = true; + + /// + /// Bool list for each foldout encountered. Supports up to 128 foldouts. + /// + private bool[] foldoutArray = new bool[128]; + protected Dictionary> renderExtensions = null; public OpenGraphGUIEditor() { renderExtensions = null; + for(int i = 0; i < foldoutArray.Length; i++) + { + //Initialize foldouts to show by default + foldoutArray[i] = true; + } } //BASE GUI STRUCTURE @@ -104,6 +124,8 @@ public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] pro fieldCenteredMode = false; fieldExpandedMode = false; + hadOneFoldout = false; + currentlyInFoldout = false; SetUtilityLabelWidth(); RenderPropertiesList(properties); @@ -120,6 +142,9 @@ public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] pro /// void RenderPropertiesList(MaterialProperty[] properties) { + //Track count of foldouts encountered + int foldoutCount = 0; + //Track if the last property was non-texture type or //contained a non-null input texture bool lastWasPopulated = true; @@ -147,8 +172,17 @@ void RenderPropertiesList(MaterialProperty[] properties) currentLinkedProperties.Add(thisProp.displayName, link); } + else if (thisProp.displayName.StartsWith(foldoutPrefix)) + { + //This is a foldout property + + foldoutCount++; + } } + //Reset current foldout to 0 + currentFoldoutIndex = 0; + //Now iterate across the properties for real and render them for (int i = 0; i < properties.Length; i++) { @@ -192,7 +226,7 @@ void RenderPropertiesList(MaterialProperty[] properties) //Use min field width and don't render this property SetFieldExpandedMode(false); } - else if(currentLinkedProperties.ContainsKey(propName)) + else if(currentLinkedProperties.ContainsKey(propName) && DoRenderProp()) { //This is a linked property, so check if it was rendered already var thisLinkedProp = currentLinkedProperties[propName]; @@ -210,7 +244,51 @@ void RenderPropertiesList(MaterialProperty[] properties) RenderVisibleProperty(thisLinkedProp.matProperty, propName, i); } } - else if(propName.StartsWith(labelPrefix)) + else if (propName.StartsWith(foldoutPrefix)) + { + //This is a foldout type, so create a new group + + //Trim the foldout prefix + propName = propName.Substring(foldoutPrefix.Length); + if(propName.Trim().Length > 0) + { + //There is a foldout name to use + + if(currentlyInFoldout) + { + //Stop the previous foldout + EditorGUILayout.EndFoldoutHeaderGroup(); + } + + //Update the current foldout index to the new value before setting it + currentFoldoutIndex++; + //This is done first so that later on, DoRenderProp() can tell if the last foldout is unfolded, + //While still allowing this value to change when it encountered a new foldout. + + //This actually means the first item (0) will be skipped + //But that's okay because it is never referenced. + + //Render the foldout + foldoutArray[currentFoldoutIndex] = EditorGUILayout.BeginFoldoutHeaderGroup(foldoutArray[currentFoldoutIndex], propName); + + //Finally, track that we encountered at least one foldout + hadOneFoldout = true; + //And tell the next properties that we are in a foldout + currentlyInFoldout = true; + + } + else + { + //End the last foldout if there is one + if(currentlyInFoldout) + { + EditorGUILayout.EndFoldoutHeaderGroup(); + currentlyInFoldout = false; + } + } + + } + else if(propName.StartsWith(labelPrefix) && DoRenderProp()) { //This is a label type, so show a bold header instead of the property @@ -219,7 +297,7 @@ void RenderPropertiesList(MaterialProperty[] properties) RenderLabelProperty(propName); } - else if (propName.StartsWith(dependentVisibleTextPrefix)) + else if (propName.StartsWith(dependentVisibleTextPrefix) && DoRenderProp()) { //It is dependent, so we will conditionally render it @@ -235,7 +313,7 @@ void RenderPropertiesList(MaterialProperty[] properties) //Don't render this property } } - else + else if (DoRenderProp()) { //It's not dependent, so update populated state based on this if (thisProp.type == MaterialProperty.PropType.Texture) @@ -260,11 +338,27 @@ void RenderPropertiesList(MaterialProperty[] properties) /// void RenderBottomOptions() { - matEditor.RenderQueueField(); + //If we had one foldout earlier, draw this as it's own foldout group + if(hadOneFoldout) + { + if(currentlyInFoldout) + { + EditorGUILayout.EndFoldoutHeaderGroup(); + } + bottomOptionsFoldout = EditorGUILayout.BeginFoldoutHeaderGroup(bottomOptionsFoldout, "Advanced"); + } + + //If we don't use the group OR we do & it's unfolded, show the options + if(!hadOneFoldout || bottomOptionsFoldout) + { + matEditor.RenderQueueField(); - matEditor.EnableInstancingField(); - matEditor.DoubleSidedGIField(); - matEditor.EmissionEnabledProperty(); + matEditor.EnableInstancingField(); + matEditor.DoubleSidedGIField(); + matEditor.EmissionEnabledProperty(); + //Lightmap Emission may be a built-in only concept(?) + //matEditor.LightmapEmissionProperty(); + } } //PROPERTY RENDERING @@ -279,6 +373,7 @@ void RenderBottomOptions() /// void RenderDependentVisibleProperty(MaterialProperty v, string labelName, int index) { + //Shift over by a small amount to show the dependency EditorGUILayout.BeginHorizontal(); @@ -309,7 +404,7 @@ void RenderDependentVisibleProperty(MaterialProperty v, string labelName, int in void RenderVisibleProperty(MaterialProperty v, string labelName, int index) { - if(labelName.StartsWith(singleLineTexPrefix)) + if (labelName.StartsWith(singleLineTexPrefix)) { if(v.type == MaterialProperty.PropType.Texture) { @@ -360,6 +455,7 @@ void RenderVisibleProperty(MaterialProperty v, string labelName, int index) /// void RenderDefaultPropertyView(MaterialProperty v, string customName = "") { + string finalName = (customName == "") ? v.displayName : customName; switch(v.type) @@ -394,9 +490,22 @@ void RenderDefaultPropertyView(MaterialProperty v, string customName = "") /// void RenderLabelProperty(string propName) { + EditorGUILayout.Space(labelTopSpace); EditorGUILayout.LabelField(propName, EditorStyles.boldLabel); } + //QUERY + + /// + /// Check if we should render the current prop + /// based on foldout status. + /// + /// + bool DoRenderProp() + { + return (!currentlyInFoldout || foldoutArray[currentFoldoutIndex]); + } + //EDITOR GUI /// diff --git a/README.md b/README.md index 1ccc3c2..54126be 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ The default Inspector view for ShaderGraph-based materials can be somewhat bland ## Why use OpenGraphGUI? -**OpenGraphGUI** lets you customize the appearance of your material properties right from ShaderGraph. Simply prefix your property names in the Shader's Blackboard with certain special characters, and any material using that Shader will display the custom GUI. +**OpenGraphGUI** lets you customize the appearance of your material properties right from ShaderGraph. Simply prefix your property names in the Shader's Blackboard with certain special characters, and any material using that Shader will display the custom GUI. This is a lightweight and easy to use script that provides more control over the design of your Shaders. @@ -37,6 +37,10 @@ Use a property called **\[Centered\]** to adjust the spacing of the Inspector fi +**New!** Use the **hashtag symbol (#)** as a prefix for one of your properties and you will create a Foldout Group with your property name as the title. Create up to 128 unique foldout groups by creating new foldout properties in sequence without having to tag the end of each group. If you do wish to close the previous Foldout Group, simply create a property that consists of only a **hashtag symbol (#)** without any name. The following properties will exist outside of the last group. + + + ### Property Rendering Features Prefix a texture property with the **percent symbol (%)** and it will show as a single line texture property instead of the big thumbnail version. Single line textures are commonly used in the built-in materials and offer a cleaner look that takes up less space in your Inspector. diff --git a/Samples~/DrawPropertyExtension/OpenGraphPropertyExtension.cs b/Samples~/DrawPropertyExtension/OpenGraphPropertyExtension.cs index 2da3570..d5cb3d5 100644 --- a/Samples~/DrawPropertyExtension/OpenGraphPropertyExtension.cs +++ b/Samples~/DrawPropertyExtension/OpenGraphPropertyExtension.cs @@ -18,7 +18,7 @@ public class RPOpenGraphGUIExtension : RPOpenGraphGUI /// There is one property which we can override to add our extension. /// This is done from within the constructor to improve performance. /// - public RPOpenGraphGUIExtension() + public RPOpenGraphGUIExtension() : base() { //First initialize the property renderExtensions = new Dictionary>(); diff --git a/package.json b/package.json index b9d14fe..f939e66 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "com.robproductions.opengraphgui", - "version": "1.1.0", + "version": "1.2.0", "displayName": "Open Graph GUI", "description": "An open-source generic Shader GUI for use with URP ShaderGraphs. This package aims to help developers clean up the look of their Material properties while maintaining the built-in style.", "unity": "2020.3",