// Amplify Shader Editor - Visual Shader Editing Tool // Copyright (c) Amplify Creations, Lda using System; using UnityEngine; using UnityEditor; namespace AmplifyShaderEditor { [Serializable] [NodeAttributes( "Outline", "Miscellaneous", "Uses vertices to simulate an outline around the object" )] public sealed class OutlineNode : ParentNode { enum OutlineAlphaModes { None = 0, Masked, Transparent, AlphaPremultiplied }; private const string CullModePortNameStr = "Cull Mode"; private const string AlphaModePortNameStr = "Alpha"; private const string MaskedModePortNamStr = "Opacity Mask"; private const string OutlineAlphaModeStr = "Alpha Mode"; private const string OpacityMaskClipValueStr = "Mask Clip Value"; private const string ErrorMessage = "Outline node should only be connected to vertex ports."; [SerializeField] private bool m_noFog = true; [SerializeField] private string[] AvailableOutlineModes = { "Vertex Offset", "Vertex Scale", "Custom" }; [SerializeField] private int[] AvailableOutlineValues = { 0, 1, 2 }; [SerializeField] private int m_currentSelectedMode = 0; [SerializeField] private OutlineAlphaModes m_currentAlphaMode = OutlineAlphaModes.None; private UpperLeftWidgetHelper m_upperLeftWidget = new UpperLeftWidgetHelper(); [NonSerialized] private StandardSurfaceOutputNode m_masterNode = null; [SerializeField] private int m_zTestMode = 0; [SerializeField] private int m_zWriteMode = 0; [SerializeField] private CullMode m_cullMode = CullMode.Front; protected override void CommonInit( int uniqueId ) { base.CommonInit( uniqueId ); AddOutputPort( WirePortDataType.FLOAT3, "Out" ); AddInputPort( WirePortDataType.FLOAT3, false, "Color", -1, MasterNodePortCategory.Fragment, 0 ); AddInputPort( WirePortDataType.FLOAT, false, "Alpha", -1, MasterNodePortCategory.Fragment, 2 ); AddInputPort( WirePortDataType.FLOAT, false, "Width", -1, MasterNodePortCategory.Fragment, 1 ); GetInputPortByUniqueId( 2 ).Visible = false; m_textLabelWidth = 115; m_hasLeftDropdown = true; SetAdditonalTitleText( string.Format( Constants.SubTitleTypeFormatStr, AvailableOutlineModes[ m_currentSelectedMode ] ) ); } public override void PropagateNodeData( NodeData nodeData, ref MasterNodeDataCollector dataCollector ) { base.PropagateNodeData( nodeData, ref dataCollector ); if( GetInputPortByUniqueId( 0 ).IsConnected ) dataCollector.UsingCustomOutlineColor = true; if( GetInputPortByUniqueId( 1 ).IsConnected ) dataCollector.UsingCustomOutlineWidth = true; if( GetInputPortByUniqueId( 2 ).IsConnected ) dataCollector.UsingCustomOutlineAlpha = true; if( !dataCollector.IsTemplate ) { UIUtils.CurrentWindow.OutsideGraph.CurrentStandardSurface.OutlineHelper.ZWriteMode = m_zWriteMode; UIUtils.CurrentWindow.OutsideGraph.CurrentStandardSurface.OutlineHelper.OffsetMode = m_currentSelectedMode; } } public override void AfterCommonInit() { base.AfterCommonInit(); if( PaddingTitleLeft == 0 ) { PaddingTitleLeft = Constants.PropertyPickerWidth + Constants.IconsLeftRightMargin; if( PaddingTitleRight == 0 ) PaddingTitleRight = Constants.PropertyPickerWidth + Constants.IconsLeftRightMargin; } } public override void Destroy() { base.Destroy(); m_upperLeftWidget = null; } public override void Draw( DrawInfo drawInfo ) { base.Draw( drawInfo ); EditorGUI.BeginChangeCheck(); m_currentSelectedMode = m_upperLeftWidget.DrawWidget( this, m_currentSelectedMode, AvailableOutlineModes ); if( EditorGUI.EndChangeCheck() ) { SetAdditonalTitleText( string.Format( Constants.SubTitleTypeFormatStr, AvailableOutlineModes[ m_currentSelectedMode ] ) ); UpdatePorts(); } } void CheckAlphaPortVisibility() { InputPort alphaPort = GetInputPortByUniqueId( 2 ); if( m_currentAlphaMode != OutlineAlphaModes.None ) { if( !alphaPort.Visible ) alphaPort.Visible = true; if( m_currentAlphaMode == OutlineAlphaModes.Masked ) { GetInputPortByUniqueId( 2 ).Name = MaskedModePortNamStr; } else { GetInputPortByUniqueId( 2 ).Name = AlphaModePortNameStr; } m_sizeIsDirty = true; } if( m_currentAlphaMode == OutlineAlphaModes.None && alphaPort.Visible ) { alphaPort.Visible = false; m_sizeIsDirty = true; } } public override void DrawProperties() { base.DrawProperties(); NodeUtils.DrawPropertyGroup( ref m_propertiesFoldout, Constants.ParameterLabelStr, () => { EditorGUI.BeginChangeCheck(); m_currentSelectedMode = EditorGUILayoutIntPopup( "Type", m_currentSelectedMode, AvailableOutlineModes, AvailableOutlineValues ); if( EditorGUI.EndChangeCheck() ) { SetAdditonalTitleText( string.Format( Constants.SubTitleTypeFormatStr, AvailableOutlineModes[ m_currentSelectedMode ] ) ); UpdatePorts(); } EditorGUI.BeginChangeCheck(); m_currentAlphaMode = (OutlineAlphaModes)EditorGUILayoutEnumPopup( OutlineAlphaModeStr, m_currentAlphaMode ); if( EditorGUI.EndChangeCheck() ) { CheckAlphaPortVisibility(); } if( m_currentAlphaMode == OutlineAlphaModes.Masked ) { if( m_masterNode == null ) { m_masterNode = UIUtils.CurrentWindow.OutsideGraph.CurrentMasterNode as StandardSurfaceOutputNode; } if( m_masterNode != null ) { m_masterNode.ShowOpacityMaskValueUI(); } } m_cullMode = (CullMode)EditorGUILayoutEnumPopup( CullModePortNameStr, m_cullMode ); m_zWriteMode = EditorGUILayoutPopup( ZBufferOpHelper.ZWriteModeStr, m_zWriteMode, ZBufferOpHelper.ZWriteModeValues ); m_zTestMode = EditorGUILayoutPopup( ZBufferOpHelper.ZTestModeStr, m_zTestMode, ZBufferOpHelper.ZTestModeLabels ); m_noFog = EditorGUILayoutToggle( "No Fog", m_noFog ); } ); } void UpdatePorts() { if( m_currentSelectedMode == 2 ) //custom mode { GetInputPortByUniqueId( 1 ).ChangeProperties( "Offset", WirePortDataType.FLOAT3, false ); } else { GetInputPortByUniqueId( 1 ).ChangeProperties( "Width", WirePortDataType.FLOAT, false ); } } public override string GenerateShaderForOutput( int outputId, ref MasterNodeDataCollector dataCollector, bool ignoreLocalvar ) { if( dataCollector.IsTemplate ) return m_outputPorts[0].ErrorValue; if( dataCollector.IsFragmentCategory ) { UIUtils.ShowMessage( UniqueId, ErrorMessage ); return m_outputPorts[ 0 ].ErrorValue; } if( m_outputPorts[ 0 ].IsLocalValue( dataCollector.PortCategory ) ) return m_outputPorts[ 0 ].ErrorValue; m_outputPorts[ 0 ].SetLocalValue( "0", dataCollector.PortCategory ); StandardSurfaceOutputNode masterNode = UIUtils.CurrentWindow.OutsideGraph.CurrentMasterNode as StandardSurfaceOutputNode; MasterNodeDataCollector outlineDataCollector = new MasterNodeDataCollector(); outlineDataCollector.IsOutlineDataCollector = true; outlineDataCollector.DirtyNormal = true; InputPort colorPort = GetInputPortByUniqueId( 0 ); InputPort alphaPort = GetInputPortByUniqueId( 2 ); InputPort vertexPort = GetInputPortByUniqueId( 1 ); //if( vertexPort.IsConnected ) //{ // outlineDataCollector.PortCategory = MasterNodePortCategory.Vertex; // string outlineWidth = vertexPort.GenerateShaderForOutput( ref outlineDataCollector, vertexPort.DataType, true, true ); // outlineDataCollector.AddToVertexLocalVariables( UniqueId, PrecisionType.Float, vertexPort.DataType, "outlineVar", outlineWidth ); // outlineDataCollector.AddVertexInstruction( outlineDataCollector.SpecialLocalVariables, UniqueId, false ); // outlineDataCollector.ClearSpecialLocalVariables(); // outlineDataCollector.AddVertexInstruction( outlineDataCollector.VertexLocalVariables, UniqueId, false ); // outlineDataCollector.ClearVertexLocalVariables(); // // need to check whether this breaks other outputs or not // UIUtils.CurrentWindow.OutsideGraph.ResetNodesLocalVariables(); //} outlineDataCollector.PortCategory = MasterNodePortCategory.Fragment; string outlineColor = colorPort.GeneratePortInstructions( ref outlineDataCollector );// "\to.Emission = " + colorPort.GeneratePortInstructions( ref outlineDataCollector ) + ";"; string alphaValue = alphaPort.Visible ? alphaPort.GeneratePortInstructions( ref outlineDataCollector ) : string.Empty; bool addTabs = outlineDataCollector.DirtySpecialLocalVariables || alphaPort.Available; outlineDataCollector.AddInstructions( "\t" + outlineDataCollector.SpecialLocalVariables.TrimStart( '\t' ) ); outlineDataCollector.ClearSpecialLocalVariables(); outlineDataCollector.AddInstructions( ( addTabs ? "\t\t\t" : "" ) + "o.Emission = " + outlineColor + ";" ); if( alphaPort.Visible ) { if( m_currentAlphaMode == OutlineAlphaModes.Masked ) { float maskClipValue = 0.5f; if( masterNode != null ) maskClipValue = masterNode.OpacityMaskClipValue; if( masterNode.InlineOpacityMaskClipValue.IsValid ) { RangedFloatNode fnode = UIUtils.GetNode( masterNode.InlineOpacityMaskClipValue.NodeId ) as RangedFloatNode; if( fnode != null ) { outlineDataCollector.AddToProperties( fnode.UniqueId, fnode.GetPropertyValue(), fnode.OrderIndex ); outlineDataCollector.AddToUniforms( fnode.UniqueId, fnode.GetUniformValue() ); } else { IntNode inode = UIUtils.GetNode( masterNode.InlineOpacityMaskClipValue.NodeId ) as IntNode; outlineDataCollector.AddToProperties( inode.UniqueId, inode.GetPropertyValue(), inode.OrderIndex ); outlineDataCollector.AddToUniforms( inode.UniqueId, inode.GetUniformValue() ); } } else { outlineDataCollector.AddToProperties( -1, string.Format( IOUtils.MaskClipValueProperty, OpacityMaskClipValueStr, maskClipValue ), -1 ); outlineDataCollector.AddToUniforms( -1, string.Format( IOUtils.MaskClipValueUniform, maskClipValue ) ); } outlineDataCollector.AddInstructions( ( addTabs ? "\n\t\t\t" : "" ) + "clip( " + alphaValue + " - " + masterNode.InlineOpacityMaskClipValue.GetValueOrProperty( IOUtils.MaskClipValueName, false ) + " );" ); } else { outlineDataCollector.AddInstructions( ( addTabs ? "\n\t\t\t" : "" ) + "o.Alpha = " + alphaValue + ";" ); } } if( outlineDataCollector.UsingWorldNormal ) outlineDataCollector.AddInstructions( ( addTabs ? "\n\t\t\t" : "" ) + "o.Normal = float3(0,0,-1);" ); //Moved vertex port code generation from ln.227 to this after fragment ones //to correctly include vertex instructions generated by them UIUtils.CurrentWindow.OutsideGraph.ResetNodesLocalVariables(); outlineDataCollector.PortCategory = MasterNodePortCategory.Vertex; string outlineWidth = vertexPort.GenerateShaderForOutput( ref outlineDataCollector, vertexPort.DataType, true, true ); outlineDataCollector.AddToVertexLocalVariables( UniqueId, PrecisionType.Float, vertexPort.DataType, "outlineVar", outlineWidth ); outlineDataCollector.AddVertexInstruction( outlineDataCollector.SpecialLocalVariables, UniqueId, false ); outlineDataCollector.ClearSpecialLocalVariables(); outlineDataCollector.AddVertexInstruction( outlineDataCollector.VertexLocalVariables, UniqueId, false ); outlineDataCollector.ClearVertexLocalVariables(); outlineDataCollector.AddASEMacros(); // need to check whether this breaks other outputs or not UIUtils.CurrentWindow.OutsideGraph.ResetNodesLocalVariables(); if( masterNode != null ) { masterNode.CheckSamplingMacrosFlag(); //masterNode.AdditionalIncludes.AddToDataCollector( ref outlineDataCollector ); //masterNode.AdditionalPragmas.AddToDataCollector( ref outlineDataCollector ); //masterNode.AdditionalDefines.AddToDataCollector( ref outlineDataCollector ); if( !masterNode.CustomShadowCaster ) masterNode.AdditionalDirectives.AddAllToDataCollector( ref outlineDataCollector ); } UIUtils.CurrentWindow.OutsideGraph.CurrentStandardSurface.OutlineHelper.InputList = outlineDataCollector.InputList; UIUtils.CurrentWindow.OutsideGraph.CurrentStandardSurface.OutlineHelper.Inputs = outlineDataCollector.Inputs; UIUtils.CurrentWindow.OutsideGraph.CurrentStandardSurface.OutlineHelper.DirtyInput = outlineDataCollector.DirtyInputs; UIUtils.CurrentWindow.OutsideGraph.CurrentStandardSurface.OutlineHelper.Includes = outlineDataCollector.Includes; UIUtils.CurrentWindow.OutsideGraph.CurrentStandardSurface.OutlineHelper.Pragmas = outlineDataCollector.Pragmas; UIUtils.CurrentWindow.OutsideGraph.CurrentStandardSurface.OutlineHelper.Defines = outlineDataCollector.Defines; UIUtils.CurrentWindow.OutsideGraph.CurrentStandardSurface.OutlineHelper.StandardAdditionalDirectives = outlineDataCollector.StandardAdditionalDirectives; UIUtils.CurrentWindow.OutsideGraph.CurrentStandardSurface.OutlineHelper.Uniforms = outlineDataCollector.Uniforms; UIUtils.CurrentWindow.OutsideGraph.CurrentStandardSurface.OutlineHelper.InstancedProperties = outlineDataCollector.InstancedProperties; UIUtils.CurrentWindow.OutsideGraph.CurrentStandardSurface.OutlineHelper.GrabPasses = outlineDataCollector.GrabPass; UIUtils.CurrentWindow.OutsideGraph.CurrentStandardSurface.OutlineHelper.UniformList = outlineDataCollector.UniformsList; UIUtils.CurrentWindow.OutsideGraph.CurrentStandardSurface.OutlineHelper.InstancedPropertiesList = outlineDataCollector.InstancedPropertiesList; UIUtils.CurrentWindow.OutsideGraph.CurrentStandardSurface.OutlineHelper.VertexData = outlineDataCollector.VertexData; UIUtils.CurrentWindow.OutsideGraph.CurrentStandardSurface.OutlineHelper.Instructions = outlineDataCollector.Instructions; UIUtils.CurrentWindow.OutsideGraph.CurrentStandardSurface.OutlineHelper.Functions = outlineDataCollector.Functions; UIUtils.CurrentWindow.OutsideGraph.CurrentStandardSurface.OutlineHelper.LocalFunctions = outlineDataCollector.LocalFunctions; UIUtils.CurrentWindow.OutsideGraph.CurrentStandardSurface.OutlineHelper.OutlineCullMode = m_cullMode; UIUtils.CurrentWindow.OutsideGraph.CurrentStandardSurface.OutlineHelper.ZTestMode = m_zTestMode; UIUtils.CurrentWindow.OutsideGraph.CurrentStandardSurface.OutlineHelper.ZWriteMode = m_zWriteMode; UIUtils.CurrentWindow.OutsideGraph.CurrentStandardSurface.OutlineHelper.OffsetMode = m_currentSelectedMode; UIUtils.CurrentWindow.OutsideGraph.CurrentStandardSurface.OutlineHelper.CustomNoFog = m_noFog; dataCollector.CustomOutlineSelectedAlpha = (int)m_currentAlphaMode; for( int i = 0; i < outlineDataCollector.PropertiesList.Count; i++ ) { dataCollector.AddToProperties( UniqueId, outlineDataCollector.PropertiesList[ i ].PropertyName, outlineDataCollector.PropertiesList[ i ].OrderIndex ); } dataCollector.UsingInternalData = dataCollector.UsingInternalData || outlineDataCollector.UsingInternalData; UIUtils.CurrentWindow.OutsideGraph.ResetNodesLocalVariablesIfNot( MasterNodePortCategory.Vertex ); return "0"; } public override void ReadFromString( ref string[] nodeParams ) { base.ReadFromString( ref nodeParams ); m_currentSelectedMode = Convert.ToInt32( GetCurrentParam( ref nodeParams ) ); m_noFog = Convert.ToBoolean( GetCurrentParam( ref nodeParams ) ); if( UIUtils.CurrentShaderVersion() > 14202 ) m_currentAlphaMode = (OutlineAlphaModes)Enum.Parse( typeof( OutlineAlphaModes ), GetCurrentParam( ref nodeParams ) ); if( UIUtils.CurrentShaderVersion() > 14302 ) { m_zWriteMode = Convert.ToInt32( GetCurrentParam( ref nodeParams ) ); m_zTestMode = Convert.ToInt32( GetCurrentParam( ref nodeParams ) ); } if( UIUtils.CurrentShaderVersion() > 15304 ) { m_cullMode = (CullMode)Enum.Parse( typeof( CullMode ), GetCurrentParam( ref nodeParams ) ); } SetAdditonalTitleText( string.Format( Constants.SubTitleTypeFormatStr, AvailableOutlineModes[ m_currentSelectedMode ] ) ); UpdatePorts(); CheckAlphaPortVisibility(); } public override void WriteToString( ref string nodeInfo, ref string connectionsInfo ) { base.WriteToString( ref nodeInfo, ref connectionsInfo ); IOUtils.AddFieldValueToString( ref nodeInfo, m_currentSelectedMode ); IOUtils.AddFieldValueToString( ref nodeInfo, m_noFog ); IOUtils.AddFieldValueToString( ref nodeInfo, m_currentAlphaMode ); IOUtils.AddFieldValueToString( ref nodeInfo, m_zWriteMode ); IOUtils.AddFieldValueToString( ref nodeInfo, m_zTestMode ); IOUtils.AddFieldValueToString( ref nodeInfo, m_cullMode ); } } }