// Amplify Shader Editor - Visual Shader Editing Tool // Copyright (c) Amplify Creations, Lda // // Custom Node Global Array // Donated by Johann van Berkel using System; using UnityEngine; using UnityEditor; namespace AmplifyShaderEditor { [Serializable] [NodeAttributes( "Global Array", "Constants And Properties", "The node returns a value from a global array, which you can configure by entering the name of the array in the node's settings.", null, KeyCode.None, true, false, null, null, "Johann van Berkel" )] public sealed class GlobalArrayNode : ParentNode { private const string DefaultArrayName = "MyGlobalArray"; private const string TypeStr = "Type"; private const string AutoRangeCheckStr = "Range Check"; private const string ArrayFormatStr = "{0}[{1}]"; private const string JaggedArrayFormatStr = "{0}[{1}][{2}]"; private const string IsJaggedStr = "Is Jagged"; private const string AutoRegisterStr = "Auto-Register"; private readonly string[] AvailableTypesLabel = { "Float", "Color", "Vector4", "Matrix4" }; private readonly WirePortDataType[] AvailableTypesValues = { WirePortDataType.FLOAT, WirePortDataType.COLOR, WirePortDataType.FLOAT4, WirePortDataType.FLOAT4x4 }; [SerializeField] private string m_name = DefaultArrayName; [SerializeField] private int m_indexX = 0; [SerializeField] private int m_indexY = 0; [SerializeField] private int m_arrayLengthX = 1; [SerializeField] private int m_arrayLengthY = 1; [SerializeField] private int m_type = 0; [SerializeField] private bool m_autoRangeCheck = false; [SerializeField] private bool m_isJagged = false; [SerializeField] private bool m_autoRegister = false; ////////////////////////////////////////////////////////////////// private readonly Color ReferenceHeaderColor = new Color( 0.6f, 3.0f, 1.25f, 1.0f ); [SerializeField] private TexReferenceType m_referenceType = TexReferenceType.Object; [SerializeField] private int m_referenceArrayId = -1; [SerializeField] private int m_referenceNodeId = -1; private GlobalArrayNode m_referenceNode = null; private bool m_updated = false; protected override void CommonInit( int uniqueId ) { base.CommonInit( uniqueId ); AddInputPort( WirePortDataType.INT, false, "Index", -1, MasterNodePortCategory.Fragment, 0 ); AddInputPort( WirePortDataType.INT, false, "Index Y", -1, MasterNodePortCategory.Fragment, 2 ); AddInputPort( WirePortDataType.INT, false, "Array Length", -1, MasterNodePortCategory.Fragment, 1 ); AddInputPort( WirePortDataType.INT, false, "Array Length Y", -1, MasterNodePortCategory.Fragment, 3 ); AddOutputPort( WirePortDataType.FLOAT, "Out" ); m_textLabelWidth = 95; SetAdditonalTitleText( string.Format( Constants.SubTitleValueFormatStr, m_name ) ); UpdatePorts(); } protected override void OnUniqueIDAssigned() { base.OnUniqueIDAssigned(); UIUtils.CurrentWindow.OutsideGraph.GlobalArrayNodes.AddNode( this ); } public override void Destroy() { base.Destroy(); UIUtils.CurrentWindow.OutsideGraph.GlobalArrayNodes.RemoveNode( this ); } void UpdatePorts() { InputPort indexXPort = GetInputPortByUniqueId( 0 ); InputPort arrayLengthPortX = GetInputPortByUniqueId( 1 ); InputPort indexYPort = GetInputPortByUniqueId( 2 ); InputPort arrayLengthPortY = GetInputPortByUniqueId( 3 ); if( m_referenceType == TexReferenceType.Object ) { m_headerColorModifier = Color.white; SetAdditonalTitleText( string.Format( Constants.SubTitleValueFormatStr, m_name ) ); arrayLengthPortX.Visible = true; if( m_isJagged ) { indexXPort.Name = "Index X"; arrayLengthPortX.Name = "Array Length X"; indexYPort.Visible = true; arrayLengthPortY.Visible = true; } else { indexXPort.Name = "Index"; arrayLengthPortX.Name = "Array Length"; indexYPort.Visible = false; arrayLengthPortY.Visible = false; } } else if( m_referenceNodeId > -1 ) { m_headerColorModifier = ReferenceHeaderColor; if( m_referenceNode == null ) m_referenceNode = UIUtils.GetNode( m_referenceNodeId ) as GlobalArrayNode; if( m_referenceNode != null ) { SetAdditonalTitleText( string.Format( Constants.SubTitleValueFormatStr, m_referenceNode.DataToArray ) ); arrayLengthPortX.Visible = false; arrayLengthPortY.Visible = false; if( m_referenceNode.IsJagged ) { indexXPort.Name = "Index X"; indexYPort.Visible = true; } else { indexXPort.Name = "Index"; indexYPort.Visible = false; } } } m_sizeIsDirty = true; } void DrawObjectProperties() { EditorGUI.BeginChangeCheck(); m_name = EditorGUILayoutStringField( "Name", m_name ); if( EditorGUI.EndChangeCheck() ) { m_updated = true; m_name = UIUtils.RemoveInvalidCharacters( m_name ); if( string.IsNullOrEmpty( m_name ) ) m_name = DefaultArrayName; UIUtils.UpdateGlobalArrayDataNode( UniqueId, m_name ); SetAdditonalTitleText( string.Format( Constants.SubTitleValueFormatStr, m_name ) ); } m_autoRegister = EditorGUILayoutToggle( AutoRegisterStr, m_autoRegister ); EditorGUI.BeginChangeCheck(); m_isJagged = EditorGUILayoutToggle( IsJaggedStr, m_isJagged ); if( EditorGUI.EndChangeCheck() ) { m_updated = true; UpdatePorts(); } InputPort indexXPort = GetInputPortByUniqueId( 0 ); if( !indexXPort.IsConnected ) { EditorGUI.BeginChangeCheck(); m_indexX = EditorGUILayoutIntField( indexXPort.Name, m_indexX ); if( EditorGUI.EndChangeCheck() ) { m_indexX = Mathf.Clamp( m_indexX, 0, ( m_arrayLengthX - 1 ) ); } } if( m_isJagged ) { InputPort indexYPort = GetInputPortByUniqueId( 2 ); if( !indexYPort.IsConnected ) { EditorGUI.BeginChangeCheck(); m_indexY = EditorGUILayoutIntField( indexYPort.Name, m_indexY ); if( EditorGUI.EndChangeCheck() ) { m_indexY = Mathf.Clamp( m_indexY, 0, ( m_arrayLengthY - 1 ) ); } } } InputPort arrayLengthXPort = GetInputPortByUniqueId( 1 ); if( !arrayLengthXPort.IsConnected ) { EditorGUI.BeginChangeCheck(); m_arrayLengthX = EditorGUILayoutIntField( arrayLengthXPort.Name, m_arrayLengthX ); if( EditorGUI.EndChangeCheck() ) { m_arrayLengthX = Mathf.Max( 1, m_arrayLengthX ); } } if( m_isJagged ) { InputPort arrayLengthYPort = GetInputPortByUniqueId( 3 ); if( !arrayLengthYPort.IsConnected ) { EditorGUI.BeginChangeCheck(); m_arrayLengthY = EditorGUILayoutIntField( arrayLengthYPort.Name, m_arrayLengthY ); if( EditorGUI.EndChangeCheck() ) { m_arrayLengthY = Mathf.Max( 1, m_arrayLengthY ); } } } EditorGUI.BeginChangeCheck(); m_type = EditorGUILayoutPopup( TypeStr, m_type, AvailableTypesLabel ); if( EditorGUI.EndChangeCheck() ) { m_outputPorts[ 0 ].ChangeType( (WirePortDataType)AvailableTypesValues[ m_type ], false ); } m_autoRangeCheck = EditorGUILayoutToggle( AutoRangeCheckStr, m_autoRangeCheck ); } public override void OnNodeLayout( DrawInfo drawInfo ) { base.OnNodeLayout( drawInfo ); m_updated = false; if( m_referenceType == TexReferenceType.Instance ) { if( m_referenceNodeId > -1 && m_referenceNode == null ) { m_referenceNode = UIUtils.GetNode( m_referenceNodeId ) as GlobalArrayNode; if( m_referenceNode == null ) { m_referenceNodeId = -1; } } if( m_referenceNode != null && m_referenceNode.Updated) { UpdatePorts(); } } } void DrawInstancedProperties() { string[] arr = UIUtils.GlobalArrayNodeArr(); bool guiEnabledBuffer = GUI.enabled; if( arr != null && arr.Length > 0 ) { GUI.enabled = true; } else { m_referenceArrayId = -1; m_referenceNodeId = -1; m_referenceNode = null; GUI.enabled = false; } EditorGUI.BeginChangeCheck(); m_referenceArrayId = EditorGUILayoutPopup( Constants.AvailableReferenceStr, m_referenceArrayId, arr ); if( EditorGUI.EndChangeCheck() ) { m_referenceNode = UIUtils.GetGlobalArrayNode( m_referenceArrayId ); if( m_referenceNode != null ) { m_referenceNodeId = m_referenceNode.UniqueId; } UpdatePorts(); } GUI.enabled = guiEnabledBuffer; InputPort indexXPort = GetInputPortByUniqueId( 0 ); if( !indexXPort.IsConnected ) { EditorGUI.BeginChangeCheck(); m_indexX = EditorGUILayoutIntField( indexXPort.Name, m_indexX ); if( EditorGUI.EndChangeCheck() ) { m_indexX = Mathf.Clamp( m_indexX, 0, ( m_arrayLengthX - 1 ) ); } } if( m_isJagged ) { InputPort indexYPort = GetInputPortByUniqueId( 2 ); if( !indexYPort.IsConnected ) { EditorGUI.BeginChangeCheck(); m_indexY = EditorGUILayoutIntField( indexYPort.Name, m_indexY ); if( EditorGUI.EndChangeCheck() ) { m_indexY = Mathf.Clamp( m_indexY, 0, ( m_arrayLengthY - 1 ) ); } } } } public override void DrawProperties() { EditorGUI.BeginChangeCheck(); m_referenceType = (TexReferenceType)EditorGUILayoutPopup( Constants.ReferenceTypeStr, (int)m_referenceType, Constants.ReferenceArrayLabels ); if( EditorGUI.EndChangeCheck() ) { UpdatePorts(); } if( m_referenceType == TexReferenceType.Object ) DrawObjectProperties(); else DrawInstancedProperties(); } public string GetArrayValue( string indexX, string indexY = null ) { if( m_isJagged ) return string.Format( JaggedArrayFormatStr, m_name, indexX, indexY ); return string.Format( ArrayFormatStr, m_name, indexX ); } public string GenerateInstancedShaderForOutput( int outputId, ref MasterNodeDataCollector dataCollector, bool ignoreLocalvar ) { string result = string.Empty; if( m_referenceNode != null ) { InputPort indexXPort = GetInputPortByUniqueId( 0 ); if( m_referenceNode.IsJagged ) { InputPort indexYPort = GetInputPortByUniqueId( 2 ); string arrayIndexX = indexXPort.IsConnected ? indexXPort.GeneratePortInstructions( ref dataCollector ) : m_indexX.ToString(); string arrayIndexY = indexYPort.IsConnected ? indexYPort.GeneratePortInstructions( ref dataCollector ) : m_indexY.ToString(); result = m_referenceNode.GetArrayValue( arrayIndexX, arrayIndexY ); } else { string arrayIndexX = indexXPort.IsConnected ? indexXPort.GeneratePortInstructions( ref dataCollector ) : m_indexX.ToString(); result = m_referenceNode.GetArrayValue( arrayIndexX ); } } m_outputPorts[ 0 ].SetLocalValue( result, dataCollector.PortCategory ); return result; } public override string GenerateShaderForOutput( int outputId, ref MasterNodeDataCollector dataCollector, bool ignoreLocalvar ) { if( m_outputPorts[ 0 ].IsLocalValue( dataCollector.PortCategory ) ) return m_outputPorts[ 0 ].LocalValue( dataCollector.PortCategory ); if( m_referenceType == TexReferenceType.Instance ) return GenerateInstancedShaderForOutput( outputId, ref dataCollector, ignoreLocalvar ); string dataType = UIUtils.PrecisionWirePortToCgType( CurrentPrecisionType, AvailableTypesValues[ m_type ] ); InputPort indexXPort = GetInputPortByUniqueId( 0 ); InputPort arrayLengthXPort = GetInputPortByUniqueId( 1 ); string result = string.Empty; if( m_isJagged ) { InputPort indexYPort = GetInputPortByUniqueId( 2 ); InputPort arrayLengthYPort = GetInputPortByUniqueId( 3 ); string arrayIndexX = indexXPort.IsConnected ? indexXPort.GeneratePortInstructions( ref dataCollector ) : m_indexX.ToString(); string arrayLengthX = arrayLengthXPort.IsConnected ? arrayLengthXPort.GeneratePortInstructions( ref dataCollector ) : m_arrayLengthX.ToString(); string arrayIndexY = indexYPort.IsConnected ? indexYPort.GeneratePortInstructions( ref dataCollector ) : m_indexY.ToString(); string arrayLengthY = arrayLengthYPort.IsConnected ? arrayLengthYPort.GeneratePortInstructions( ref dataCollector ) : m_arrayLengthY.ToString(); dataCollector.AddToUniforms( UniqueId, dataType, string.Format( JaggedArrayFormatStr, m_name, arrayLengthX, arrayLengthY ) ); if( m_autoRangeCheck ) { arrayIndexX = string.Format( "clamp({0},0,({1} - 1))", arrayIndexX, arrayLengthX ); arrayIndexY = string.Format( "clamp({0},0,({1} - 1))", arrayIndexY, arrayLengthY ); } result = string.Format( JaggedArrayFormatStr, m_name, arrayIndexX, arrayIndexY ); } else { string arrayIndex = indexXPort.IsConnected ? indexXPort.GeneratePortInstructions( ref dataCollector ) : m_indexX.ToString(); string arrayLength = arrayLengthXPort.IsConnected ? arrayLengthXPort.GeneratePortInstructions( ref dataCollector ) : m_arrayLengthX.ToString(); dataCollector.AddToUniforms( UniqueId, dataType, string.Format( ArrayFormatStr, m_name, arrayLength ) ); if( m_autoRangeCheck ) arrayIndex = string.Format( "clamp({0},0,({1} - 1))", arrayIndex, arrayLength ); result = string.Format( ArrayFormatStr, m_name, arrayIndex ); } m_outputPorts[ 0 ].SetLocalValue( result, dataCollector.PortCategory ); return m_outputPorts[ 0 ].LocalValue( dataCollector.PortCategory ); } public void CheckIfAutoRegister( ref MasterNodeDataCollector dataCollector ) { if( m_referenceType == TexReferenceType.Object && m_autoRegister && m_connStatus != NodeConnectionStatus.Connected ) { string dataType = UIUtils.PrecisionWirePortToCgType( CurrentPrecisionType, AvailableTypesValues[ m_type ] ); if( m_isJagged ) { dataCollector.AddToUniforms( UniqueId, dataType, string.Format( JaggedArrayFormatStr, m_name, m_arrayLengthX, m_arrayLengthY ) ); } else { dataCollector.AddToUniforms( UniqueId, dataType, string.Format( ArrayFormatStr, m_name, m_arrayLengthX ) ); } } } public override void WriteToString( ref string nodeInfo, ref string connectionsInfo ) { base.WriteToString( ref nodeInfo, ref connectionsInfo ); IOUtils.AddFieldValueToString( ref nodeInfo, m_name ); IOUtils.AddFieldValueToString( ref nodeInfo, m_indexX ); IOUtils.AddFieldValueToString( ref nodeInfo, m_arrayLengthX ); IOUtils.AddFieldValueToString( ref nodeInfo, m_type ); IOUtils.AddFieldValueToString( ref nodeInfo, m_autoRangeCheck ); IOUtils.AddFieldValueToString( ref nodeInfo, m_isJagged ); IOUtils.AddFieldValueToString( ref nodeInfo, m_indexY ); IOUtils.AddFieldValueToString( ref nodeInfo, m_arrayLengthY ); IOUtils.AddFieldValueToString( ref nodeInfo, m_autoRegister ); IOUtils.AddFieldValueToString( ref nodeInfo, m_referenceType ); IOUtils.AddFieldValueToString( ref nodeInfo, m_referenceNodeId ); } public override void ReadFromString( ref string[] nodeParams ) { base.ReadFromString( ref nodeParams ); m_name = GetCurrentParam( ref nodeParams ); m_indexX = Convert.ToInt32( GetCurrentParam( ref nodeParams ) ); m_arrayLengthX = Convert.ToInt32( GetCurrentParam( ref nodeParams ) ); m_type = Convert.ToInt32( GetCurrentParam( ref nodeParams ) ); m_autoRangeCheck = Convert.ToBoolean( GetCurrentParam( ref nodeParams ) ); if( UIUtils.CurrentShaderVersion() > 15801 ) { m_isJagged = Convert.ToBoolean( GetCurrentParam( ref nodeParams ) ); m_indexY = Convert.ToInt32( GetCurrentParam( ref nodeParams ) ); m_arrayLengthY = Convert.ToInt32( GetCurrentParam( ref nodeParams ) ); m_autoRegister = Convert.ToBoolean( GetCurrentParam( ref nodeParams ) ); m_referenceType = (TexReferenceType)Enum.Parse( typeof( TexReferenceType ), GetCurrentParam( ref nodeParams ) ); m_referenceNodeId = Convert.ToInt32( GetCurrentParam( ref nodeParams ) ); } SetAdditonalTitleText( string.Format( Constants.SubTitleValueFormatStr, m_name ) ); UpdatePorts(); } public override void RefreshExternalReferences() { base.RefreshExternalReferences(); if( m_referenceType == TexReferenceType.Instance && m_referenceNodeId > -1 ) { m_referenceNode = UIUtils.GetNode( m_referenceNodeId ) as GlobalArrayNode; if( m_referenceNode != null ) { m_referenceArrayId = UIUtils.GetGlobalArrayNodeRegisterId( m_referenceNodeId ); UpdatePorts(); } else { m_referenceNodeId = -1; } } } public bool AutoRegister { get { return m_autoRegister; } } public bool IsJagged { get { return m_isJagged; } } public bool Updated { get { return m_updated; } } public override string DataToArray { get { return m_name; } } } }