// Amplify Shader Editor - Visual Shader Editing Tool // Copyright (c) Amplify Creations, Lda using UnityEngine; using UnityEditor; using System.Collections.Generic; using UnityEditorInternal; namespace AmplifyShaderEditor { public sealed class NodeParametersWindow : MenuParent { private int m_lastSelectedNode = -1; private const string TitleStr = "Node Properties"; private GUIStyle m_nodePropertiesStyle; private GUIContent m_dummyContent = new GUIContent(); private GUIStyle m_propertyAdjustment; private ReorderableList m_functionInputsReordableList = null; private int m_functionInputsLastCount = 0; private ReorderableList m_functionSwitchesReordableList = null; private int m_functionSwitchesLastCount = 0; private ReorderableList m_functionOutputsReordableList = null; private int m_functionOutputsLastCount = 0; private ReorderableList m_propertyReordableList = null; private int m_lastCount = 0; private bool m_forceUpdate = false; [SerializeField] private List m_propertyReordableNodes = new List(); // width and height are between [0,1] and represent a percentage of the total screen area public NodeParametersWindow( AmplifyShaderEditorWindow parentWindow ) : base( parentWindow, 0, 0, 285, 0, string.Empty, MenuAnchor.TOP_LEFT, MenuAutoSize.MATCH_VERTICAL ) { SetMinimizedArea( -225, 0, 260, 0 ); } public void OnShaderFunctionLoad() { m_functionInputsReordableList = null; m_functionSwitchesReordableList = null; m_functionOutputsReordableList = null; } public bool Draw( Rect parentPosition, ParentNode selectedNode, Vector2 mousePosition, int mouseButtonId, bool hasKeyboardFocus ) { bool changeCheck = false; base.Draw( parentPosition, mousePosition, mouseButtonId, hasKeyboardFocus ); if ( m_nodePropertiesStyle == null ) { m_nodePropertiesStyle = UIUtils.GetCustomStyle( CustomStyle.NodePropertiesTitle ); m_nodePropertiesStyle.normal.textColor = m_nodePropertiesStyle.active.textColor = EditorGUIUtility.isProSkin ? new Color( 1f, 1f, 1f ) : new Color( 0f, 0f, 0f ); } if ( m_isMaximized ) { KeyCode key = Event.current.keyCode; if ( m_isMouseInside || hasKeyboardFocus ) { if ( key == ShortcutsManager.ScrollUpKey ) { m_currentScrollPos.y -= 10; if ( m_currentScrollPos.y < 0 ) { m_currentScrollPos.y = 0; } Event.current.Use(); } if ( key == ShortcutsManager.ScrollDownKey ) { m_currentScrollPos.y += 10; Event.current.Use(); } } if( m_forceUpdate ) { if( m_propertyReordableList != null ) m_propertyReordableList.ReleaseKeyboardFocus(); m_propertyReordableList = null; if ( m_functionInputsReordableList != null ) m_functionInputsReordableList.ReleaseKeyboardFocus(); m_functionInputsReordableList = null; if( m_functionSwitchesReordableList != null ) m_functionSwitchesReordableList.ReleaseKeyboardFocus(); m_functionSwitchesReordableList = null; if ( m_functionOutputsReordableList != null ) m_functionOutputsReordableList.ReleaseKeyboardFocus(); m_functionOutputsReordableList = null; m_forceUpdate = false; } GUILayout.BeginArea( m_transformedArea, m_content, m_style ); { //Draw selected node parameters if ( selectedNode != null ) { // this hack is need because without it the several FloatFields/Textfields/... would show wrong values ( different from the ones they were assigned to show ) if ( m_lastSelectedNode != selectedNode.UniqueId ) { m_lastSelectedNode = selectedNode.UniqueId; GUI.FocusControl( "" ); } EditorGUILayout.BeginVertical(); { EditorGUILayout.Separator(); if ( selectedNode.UniqueId == ParentWindow.CurrentGraph.CurrentMasterNodeId ) { m_dummyContent.text = "Output Node"; } else { if ( selectedNode.Attributes != null ) { m_dummyContent.text = selectedNode.Attributes.Name; } else if ( selectedNode is CommentaryNode ) { m_dummyContent.text = "Commentary"; } else { m_dummyContent.text = TitleStr; } } EditorGUILayout.LabelField( m_dummyContent, m_nodePropertiesStyle ); EditorGUILayout.Separator(); //UIUtils.RecordObject( selectedNode , "Changing properties on node " + selectedNode.UniqueId); m_currentScrollPos = EditorGUILayout.BeginScrollView( m_currentScrollPos, GUILayout.Width( 0 ), GUILayout.Height( 0 ) ); float labelWidth = EditorGUIUtility.labelWidth; //if( selectedNode.TextLabelWidth > 0 ) // EditorGUIUtility.labelWidth = selectedNode.TextLabelWidth; //else EditorGUIUtility.labelWidth = TransformedArea.width * 0.42f; changeCheck = selectedNode.SafeDrawProperties(); EditorGUIUtility.labelWidth = labelWidth; EditorGUILayout.EndScrollView(); } EditorGUILayout.EndVertical(); if ( changeCheck ) { if ( selectedNode.ConnStatus == NodeConnectionStatus.Connected ) ParentWindow.SetSaveIsDirty(); } } else { //Draw Graph Params EditorGUILayout.BeginVertical(); { EditorGUILayout.Separator(); EditorGUILayout.LabelField( "Graph Properties", m_nodePropertiesStyle ); EditorGUILayout.Separator(); m_currentScrollPos = EditorGUILayout.BeginScrollView( m_currentScrollPos, GUILayout.Width( 0 ), GUILayout.Height( 0 ) ); float labelWidth = EditorGUIUtility.labelWidth; EditorGUIUtility.labelWidth = 90; bool generalIsVisible = m_parentWindow.InnerWindowVariables.ExpandedGeneralShaderOptions; NodeUtils.DrawPropertyGroup( ref generalIsVisible, " General", DrawGeneralFunction ); m_parentWindow.InnerWindowVariables.ExpandedGeneralShaderOptions = generalIsVisible; AmplifyShaderFunction function = ParentWindow.CurrentGraph.CurrentShaderFunction; if( function != null ) { //function.AdditionalIncludes.Draw( ParentWindow.CurrentGraph.CurrentOutputNode ); //function.AdditionalPragmas.Draw( ParentWindow.CurrentGraph.CurrentOutputNode ); function.AdditionalDirectives.Draw( ParentWindow.CurrentGraph.CurrentOutputNode ); } bool inputIsVisible = m_parentWindow.InnerWindowVariables.ExpandedFunctionInputs; NodeUtils.DrawPropertyGroup( ref inputIsVisible, " Function Inputs", DrawFunctionInputs ); m_parentWindow.InnerWindowVariables.ExpandedFunctionInputs = inputIsVisible; bool swicthIsVisible = m_parentWindow.InnerWindowVariables.ExpandedFunctionSwitches; NodeUtils.DrawPropertyGroup( ref swicthIsVisible, " Function Switches", DrawFunctionSwitches ); m_parentWindow.InnerWindowVariables.ExpandedFunctionSwitches = swicthIsVisible; bool outputIsVisible = m_parentWindow.InnerWindowVariables.ExpandedFunctionOutputs; NodeUtils.DrawPropertyGroup( ref outputIsVisible, " Function Outputs", DrawFunctionOutputs ); m_parentWindow.InnerWindowVariables.ExpandedFunctionOutputs = outputIsVisible; bool properties = ParentWindow.InnerWindowVariables.ExpandedProperties; NodeUtils.DrawPropertyGroup( ref properties, " Material Properties", DrawFunctionProperties ); ParentWindow.InnerWindowVariables.ExpandedProperties = properties; EditorGUIUtility.labelWidth = labelWidth; EditorGUILayout.EndScrollView(); } EditorGUILayout.EndVertical(); } } // Close window area GUILayout.EndArea(); } PostDraw(); return changeCheck; } public void DrawGeneralFunction() { AmplifyShaderFunction function = ParentWindow.CurrentGraph.CurrentShaderFunction; if ( function == null ) return; float cacheWidth = EditorGUIUtility.labelWidth; EditorGUIUtility.labelWidth = 115; SerializedObject serializedObject = new UnityEditor.SerializedObject( function ); if ( serializedObject != null ) { SerializedProperty temo = serializedObject.FindProperty( "m_description" ); EditorGUILayout.PropertyField( temo, new GUIContent( " Description" ) ); SerializedProperty cat = serializedObject.FindProperty( "m_nodeCategory" ); SerializedProperty ppos = serializedObject.FindProperty( "m_previewPosition" ); EditorGUILayout.PropertyField( ppos, new GUIContent( "Preview Position" ) ); cat.intValue = ParentWindow.CurrentGraph.CurrentOutputNode.EditorGUILayoutPopup( "Category", cat.intValue, UIUtils.CategoryPresets ); if( cat.enumValueIndex == 0 ) { SerializedProperty custCat = serializedObject.FindProperty( "m_customNodeCategory" ); EditorGUILayout.PropertyField( custCat, new GUIContent( "Custom" ) ); } SerializedProperty hidden = serializedObject.FindProperty( "m_hidden" ); EditorGUILayout.PropertyField( hidden, new GUIContent( "Hidden" ) ); serializedObject.ApplyModifiedProperties(); } EditorGUIUtility.labelWidth = cacheWidth; } public void DrawFunctionInputs() { List functionInputNodes = UIUtils.FunctionInputList(); if ( m_functionInputsReordableList == null || functionInputNodes.Count != m_functionInputsLastCount ) { functionInputNodes.Sort( ( x, y ) => { return x.OrderIndex.CompareTo( y.OrderIndex ); } ); m_functionInputsReordableList = new ReorderableList( functionInputNodes, typeof( FunctionInput ), true, false, false, false ); m_functionInputsReordableList.headerHeight = 0; m_functionInputsReordableList.footerHeight = 0; m_functionInputsReordableList.showDefaultBackground = false; m_functionInputsReordableList.drawElementCallback = ( Rect rect, int index, bool isActive, bool isFocused ) => { EditorGUI.LabelField( rect, functionInputNodes[ index ].InputName ); }; m_functionInputsReordableList.onChangedCallback = ( list ) => { //for ( int i = 0; i < functionInputNodes.Count; i++ ) //{ // functionInputNodes[ i ].OrderIndex = i; //} ForceInputReorder( ref functionInputNodes ); }; m_functionInputsLastCount = m_functionInputsReordableList.count; } if ( m_functionInputsReordableList != null ) { if ( m_propertyAdjustment == null ) { m_propertyAdjustment = new GUIStyle(); m_propertyAdjustment.padding.left = 17; } EditorGUILayout.BeginVertical( m_propertyAdjustment ); m_functionInputsReordableList.DoLayoutList(); EditorGUILayout.EndVertical(); } } public void ForceInputReorder( ref List functionInputNodes ) { for( int i = 0; i < functionInputNodes.Count; i++ ) { functionInputNodes[ i ].OrderIndex = i; } } public void DrawFunctionSwitches() { List functionSwitchNodes = UIUtils.FunctionSwitchList(); if( m_functionSwitchesReordableList == null || functionSwitchNodes.Count != m_functionSwitchesLastCount ) { functionSwitchNodes.Sort( ( x, y ) => { return x.OrderIndex.CompareTo( y.OrderIndex ); } ); UIUtils.UpdateFunctionSwitchArr(); m_functionSwitchesReordableList = new ReorderableList( functionSwitchNodes, typeof( FunctionSwitch ), true, false, false, false ); m_functionSwitchesReordableList.headerHeight = 0; m_functionSwitchesReordableList.footerHeight = 0; m_functionSwitchesReordableList.showDefaultBackground = false; m_functionSwitchesReordableList.drawElementCallback = ( Rect rect, int index, bool isActive, bool isFocused ) => { EditorGUI.LabelField( rect, functionSwitchNodes[ index ].OptionLabel ); }; m_functionSwitchesReordableList.onChangedCallback = ( list ) => { ForceSwitchesReorder(ref functionSwitchNodes ); }; m_functionSwitchesLastCount = m_functionSwitchesReordableList.count; } if( m_functionSwitchesReordableList != null ) { if( m_propertyAdjustment == null ) { m_propertyAdjustment = new GUIStyle(); m_propertyAdjustment.padding.left = 17; } EditorGUILayout.BeginVertical( m_propertyAdjustment ); m_functionSwitchesReordableList.DoLayoutList(); EditorGUILayout.EndVertical(); } } public void ForceSwitchesReorder( ref List functionSwitchNodes ) { for( int i = 0; i < functionSwitchNodes.Count; i++ ) { functionSwitchNodes[ i ].OrderIndex = i; } UIUtils.UpdateFunctionSwitchArr(); } public void DrawFunctionOutputs() { List functionOutputNodes = UIUtils.FunctionOutputList(); if ( m_functionOutputsReordableList == null || functionOutputNodes.Count != m_functionOutputsLastCount ) { functionOutputNodes.Sort( ( x, y ) => { return x.OrderIndex.CompareTo( y.OrderIndex ); } ); m_functionOutputsReordableList = new ReorderableList( functionOutputNodes, typeof( FunctionOutput ), true, false, false, false ); m_functionOutputsReordableList.headerHeight = 0; m_functionOutputsReordableList.footerHeight = 0; m_functionOutputsReordableList.showDefaultBackground = false; m_functionOutputsReordableList.drawElementCallback = ( Rect rect, int index, bool isActive, bool isFocused ) => { EditorGUI.LabelField( rect, functionOutputNodes[ index ].OutputName ); }; m_functionOutputsReordableList.onChangedCallback = ( list ) => { for ( int i = 0; i < functionOutputNodes.Count; i++ ) { functionOutputNodes[ i ].OrderIndex = i; } }; m_functionOutputsLastCount = m_functionOutputsReordableList.count; } if ( m_functionOutputsReordableList != null ) { if ( m_propertyAdjustment == null ) { m_propertyAdjustment = new GUIStyle(); m_propertyAdjustment.padding.left = 17; } EditorGUILayout.BeginVertical( m_propertyAdjustment ); m_functionOutputsReordableList.DoLayoutList(); EditorGUILayout.EndVertical(); } } private void RefreshVisibleList( ref List allNodes ) { // temp reference for lambda expression List nodes = allNodes; m_propertyReordableNodes.Clear(); for( int i = 0; i < nodes.Count; i++ ) { ReordenatorNode rnode = nodes[ i ] as ReordenatorNode; if( ( rnode == null || !rnode.IsInside ) && ( !m_propertyReordableNodes.Exists( x => x.PropertyName.Equals( nodes[ i ].PropertyName ) ) ) ) m_propertyReordableNodes.Add( nodes[ i ] ); } m_propertyReordableNodes.Sort( ( x, y ) => { return x.OrderIndex.CompareTo( y.OrderIndex ); } ); } public void DrawFunctionProperties() { List nodes = UIUtils.PropertyNodesList(); if( nodes.Count != m_lastCount ) { RefreshVisibleList( ref nodes ); m_lastCount = nodes.Count; } if( m_propertyReordableList == null ) { m_propertyReordableList = new ReorderableList( m_propertyReordableNodes, typeof( PropertyNode ), true, false, false, false ) { headerHeight = 0, footerHeight = 0, showDefaultBackground = false, drawElementCallback = ( Rect rect, int index, bool isActive, bool isFocused ) => { var first = rect; first.width *= 0.60f; EditorGUI.LabelField( first, m_propertyReordableNodes[ index ].PropertyInspectorName ); var second = rect; second.width *= 0.4f; second.x += first.width; if( GUI.Button( second, m_propertyReordableNodes[ index ].PropertyName, new GUIStyle( "AssetLabel Partial" ) ) ) { UIUtils.FocusOnNode( m_propertyReordableNodes[ index ], 1, false ); } }, onReorderCallback = ( list ) => { ReorderList( ref nodes ); } }; ReorderList( ref nodes ); } if( m_propertyReordableList != null ) { if( m_propertyAdjustment == null ) { m_propertyAdjustment = new GUIStyle(); m_propertyAdjustment.padding.left = 17; } EditorGUILayout.BeginVertical( m_propertyAdjustment ); m_propertyReordableList.DoLayoutList(); EditorGUILayout.EndVertical(); } } public void ForceReordering() { List nodes = UIUtils.PropertyNodesList(); ReorderList( ref nodes ); List functionInputNodes = UIUtils.FunctionInputList(); ForceInputReorder( ref functionInputNodes ); List functionSwitchNodes = UIUtils.FunctionSwitchList(); ForceSwitchesReorder( ref functionSwitchNodes ); //RecursiveLog(); } private void RecursiveLog() { List nodes = UIUtils.PropertyNodesList(); nodes.Sort( ( x, y ) => { return x.OrderIndex.CompareTo( y.OrderIndex ); } ); for( int i = 0; i < nodes.Count; i++ ) { if( ( nodes[ i ] is ReordenatorNode ) ) ( nodes[ i ] as ReordenatorNode ).RecursiveLog(); else Debug.Log( nodes[ i ].OrderIndex + " " + nodes[ i ].PropertyName ); } } private void ReorderList( ref List nodes ) { // clear lock list before reordering because of multiple sf being used for( int i = 0; i < nodes.Count; i++ ) { ReordenatorNode rnode = nodes[ i ] as ReordenatorNode; if ( rnode != null ) rnode.RecursiveClear(); } int propoffset = 0; int count = 0; for ( int i = 0; i < m_propertyReordableNodes.Count; i++ ) { ReordenatorNode renode = m_propertyReordableNodes[ i ] as ReordenatorNode; if ( renode != null ) { if ( !renode.IsInside ) { m_propertyReordableNodes[ i ].OrderIndex = count + propoffset; if ( renode.PropertyListCount > 0 ) { propoffset += renode.RecursiveCount(); // the same reordenator can exist multiple times, apply ordering to all of them for( int j = 0; j < nodes.Count; j++ ) { ReordenatorNode pnode = ( nodes[ j ] as ReordenatorNode ); if ( pnode != null && pnode.PropertyName.Equals( renode.PropertyName ) ) { pnode.OrderIndex = renode.RawOrderIndex; pnode.RecursiveSetOrderOffset( renode.RawOrderIndex, true ); } } } else { count++; } } else { m_propertyReordableNodes[ i ].OrderIndex = 0; } } else { m_propertyReordableNodes[ i ].OrderIndex = count + propoffset; count++; } } } public override void Destroy() { base.Destroy(); m_functionInputsReordableList = null; m_functionOutputsReordableList = null; m_propertyReordableList = null; } public bool ForceUpdate { get { return m_forceUpdate; } set { m_forceUpdate = value; } } } }