You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

511 lines
18 KiB

  1. // Amplify Shader Editor - Visual Shader Editing Tool
  2. // Copyright (c) Amplify Creations, Lda <info@amplify.pt>
  3. using UnityEngine;
  4. using UnityEditor;
  5. using System;
  6. using System.Collections.Generic;
  7. namespace AmplifyShaderEditor
  8. {
  9. public enum OutlineMode
  10. {
  11. VertexOffset,
  12. VertexScale
  13. }
  14. [Serializable]
  15. public sealed class OutlineOpHelper
  16. {
  17. private string[] ModeTags =
  18. {
  19. "Tags{ }",
  20. "Tags{ \"RenderType\" = \"TransparentCutout\" \"Queue\" = \"AlphaTest+0\"}",
  21. "Tags{ \"RenderType\" = \"Transparent\" \"Queue\" = \"Transparent+0\"}",
  22. "Tags{ \"RenderType\" = \"Transparent\" \"Queue\" = \"Transparent+0\" }"
  23. };
  24. private string[] ModePragma =
  25. {
  26. string.Empty,
  27. string.Empty,
  28. "alpha:fade ",
  29. "alpha:premul "
  30. };
  31. private readonly string OutlineSurfaceConfig = "#pragma surface outlineSurf Outline {0} keepalpha noshadow noambient novertexlights nolightmap nodynlightmap nodirlightmap nometa noforwardadd vertex:outlineVertexDataFunc ";
  32. private readonly string OutlineBodyStructBegin = "struct Input {";
  33. private readonly string OutlineBodyStructDefault = "\thalf filler;";
  34. private readonly string OutlineBodyStructEnd = "};";
  35. private readonly string OutlineDefaultUniformColor = "uniform half4 _ASEOutlineColor;";
  36. private readonly string OutlineDefaultUniformWidth = "uniform half _ASEOutlineWidth;";
  37. private readonly string OutlineDefaultVertexHeader = "void outlineVertexDataFunc( inout appdata_full v, out Input o )\n\t\t{";
  38. private readonly string OutlineTessVertexHeader = "void outlineVertexDataFunc( inout appdata_full v )\n\t\t{";
  39. private readonly string OutlineDefaultVertexOutputDeclaration = "\tUNITY_INITIALIZE_OUTPUT( Input, o );";
  40. private readonly string[] OutlineSurfBody = {
  41. "\to.Emission = _ASEOutlineColor.rgb;",
  42. "\to.Alpha = 1;"
  43. };
  44. private readonly string[] OutlineBodyDefaultSurfBegin = {
  45. "}",
  46. "inline half4 LightingOutline( SurfaceOutput s, half3 lightDir, half atten ) { return half4 ( 0,0,0, s.Alpha); }",
  47. "void outlineSurf( Input i, inout SurfaceOutput o )",
  48. "{"};
  49. private readonly string[] OutlineBodyDefaultSurfEnd = {
  50. "}",
  51. "ENDCG",
  52. "\n"};
  53. private const string OutlineInstancedHeader = "#pragma multi_compile_instancing";
  54. private readonly string[] OutlineBodyInstancedBegin = {
  55. "UNITY_INSTANCING_CBUFFER_START({0})",
  56. "\tUNITY_DEFINE_INSTANCED_PROP( half4, _ASEOutlineColor )",
  57. "\tUNITY_DEFINE_INSTANCED_PROP(half, _ASEOutlineWidth)",
  58. "UNITY_INSTANCING_CBUFFER_END",
  59. "void outlineVertexDataFunc( inout appdata_full v, out Input o )",
  60. "{",
  61. "\tUNITY_INITIALIZE_OUTPUT( Input, o );"};
  62. private readonly string[] OutlineBodyInstancedEnd = {
  63. "}",
  64. "inline half4 LightingOutline( SurfaceOutput s, half3 lightDir, half atten ) { return half4 ( 0,0,0, s.Alpha); }",
  65. "void outlineSurf( Input i, inout SurfaceOutput o ) { o.Emission = UNITY_ACCESS_INSTANCED_PROP( _ASEOutlineColor ).rgb; o.Alpha = 1; }",
  66. "ENDCG",
  67. "\n"};
  68. private const string WidthVariableAccessInstanced = "UNITY_ACCESS_INSTANCED_PROP( _ASEOutlineWidth )";
  69. private const string OutlineVertexOffsetMode = "\tv.vertex.xyz += ( v.normal * {0} );";
  70. private const string OutlineVertexScaleMode = "\tv.vertex.xyz *= ( 1 + {0});";
  71. private const string OutlineVertexCustomMode = "\tv.vertex.xyz += {0};";
  72. private const string OutlineColorLabel = "Color";
  73. private const string OutlineWidthLabel = "Width";
  74. private const string ColorPropertyName = "_ASEOutlineColor";
  75. private const string WidthPropertyName = "_ASEOutlineWidth";
  76. private const string ColorPropertyDec = "_ASEOutlineColor( \"Outline Color\", Color ) = ({0})";
  77. private const string OutlinePropertyDec = "_ASEOutlineWidth( \"Outline Width\", Float ) = {0}";
  78. private const string ModePropertyStr = "Mode";
  79. private const string NoFogStr = "No Fog";
  80. private const string BillboardInstructionFormat = "\t{0};";
  81. [SerializeField]
  82. private Color m_outlineColor;
  83. [SerializeField]
  84. private float m_outlineWidth;
  85. [SerializeField]
  86. private bool m_enabled;
  87. [SerializeField]
  88. private OutlineMode m_mode = OutlineMode.VertexOffset;
  89. [SerializeField]
  90. private bool m_noFog = true;
  91. private CullMode m_cullMode = CullMode.Front;
  92. private int m_zTestMode = 0;
  93. private int m_zWriteMode = 0;
  94. private bool m_dirtyInput = false;
  95. private string m_inputs = string.Empty;
  96. private List<PropertyDataCollector> m_inputList = new List<PropertyDataCollector>();
  97. private string m_uniforms = string.Empty;
  98. private List<PropertyDataCollector> m_uniformList = new List<PropertyDataCollector>();
  99. private string m_instructions = string.Empty;
  100. private string m_functions = string.Empty;
  101. private string m_includes = string.Empty;
  102. private string m_pragmas = string.Empty;
  103. private string m_defines = string.Empty;
  104. private string m_vertexData = string.Empty;
  105. private string m_grabPasses = string.Empty;
  106. private Dictionary<string, string> m_localFunctions;
  107. //private OutlineMode m_customMode = OutlineMode.VertexOffset;
  108. private int m_offsetMode = 0;
  109. private bool m_customNoFog = true;
  110. public void Draw( ParentNode owner, GUIStyle toolbarstyle, Material mat )
  111. {
  112. Color cachedColor = GUI.color;
  113. GUI.color = new Color( cachedColor.r, cachedColor.g, cachedColor.b, 0.5f );
  114. EditorGUILayout.BeginHorizontal( toolbarstyle );
  115. GUI.color = cachedColor;
  116. owner.ContainerGraph.ParentWindow.InnerWindowVariables.OutlineActiveMode = owner.GUILayoutToggle( owner.ContainerGraph.ParentWindow.InnerWindowVariables.OutlineActiveMode , EditorVariablesManager.OutlineActiveMode.LabelName, UIUtils.MenuItemToggleStyle, GUILayout.ExpandWidth( true ) );
  117. EditorGUI.BeginChangeCheck();
  118. m_enabled = owner.EditorGUILayoutToggle( string.Empty, m_enabled, UIUtils.MenuItemEnableStyle, GUILayout.Width( 16 ) );
  119. if( EditorGUI.EndChangeCheck() )
  120. {
  121. if( m_enabled )
  122. UpdateToMaterial( mat );
  123. UIUtils.RequestSave();
  124. }
  125. EditorGUILayout.EndHorizontal();
  126. if( owner.ContainerGraph.ParentWindow.InnerWindowVariables.OutlineActiveMode )
  127. {
  128. cachedColor = GUI.color;
  129. GUI.color = new Color( cachedColor.r, cachedColor.g, cachedColor.b, ( EditorGUIUtility.isProSkin ? 0.5f : 0.25f ) );
  130. EditorGUILayout.BeginVertical( UIUtils.MenuItemBackgroundStyle );
  131. GUI.color = cachedColor;
  132. EditorGUILayout.Separator();
  133. EditorGUI.BeginDisabledGroup( !m_enabled );
  134. EditorGUI.indentLevel += 1;
  135. {
  136. m_mode = (OutlineMode)owner.EditorGUILayoutEnumPopup( ModePropertyStr, m_mode );
  137. EditorGUI.BeginChangeCheck();
  138. m_outlineColor = owner.EditorGUILayoutColorField( OutlineColorLabel, m_outlineColor );
  139. if( EditorGUI.EndChangeCheck() && mat != null )
  140. {
  141. if( mat.HasProperty( ColorPropertyName ) )
  142. {
  143. mat.SetColor( ColorPropertyName, m_outlineColor );
  144. }
  145. }
  146. EditorGUI.BeginChangeCheck();
  147. m_outlineWidth = owner.EditorGUILayoutFloatField( OutlineWidthLabel, m_outlineWidth );
  148. if( EditorGUI.EndChangeCheck() && mat != null )
  149. {
  150. if( mat.HasProperty( WidthPropertyName ) )
  151. {
  152. mat.SetFloat( WidthPropertyName, m_outlineWidth );
  153. }
  154. }
  155. m_noFog = owner.EditorGUILayoutToggle( NoFogStr, m_noFog );
  156. }
  157. EditorGUI.indentLevel -= 1;
  158. EditorGUI.EndDisabledGroup();
  159. EditorGUILayout.Separator();
  160. EditorGUILayout.EndVertical();
  161. }
  162. }
  163. public void UpdateToMaterial( Material mat )
  164. {
  165. if( mat == null )
  166. return;
  167. if( mat.HasProperty( ColorPropertyName ) )
  168. {
  169. mat.SetColor( ColorPropertyName, m_outlineColor );
  170. }
  171. if( mat.HasProperty( WidthPropertyName ) )
  172. {
  173. mat.SetFloat( WidthPropertyName, m_outlineWidth );
  174. }
  175. }
  176. public void ReadFromString( ref uint index, ref string[] nodeParams )
  177. {
  178. m_enabled = Convert.ToBoolean( nodeParams[ index++ ] );
  179. m_outlineWidth = Convert.ToSingle( nodeParams[ index++ ] );
  180. m_outlineColor = IOUtils.StringToColor( nodeParams[ index++ ] );
  181. if( UIUtils.CurrentShaderVersion() > 5004 )
  182. {
  183. m_mode = (OutlineMode)Enum.Parse( typeof( OutlineMode ), nodeParams[ index++ ] );
  184. }
  185. if( UIUtils.CurrentShaderVersion() > 13902 )
  186. {
  187. m_noFog = Convert.ToBoolean( nodeParams[ index++ ] );
  188. }
  189. }
  190. public void WriteToString( ref string nodeInfo )
  191. {
  192. IOUtils.AddFieldValueToString( ref nodeInfo, m_enabled );
  193. IOUtils.AddFieldValueToString( ref nodeInfo, m_outlineWidth );
  194. IOUtils.AddFieldValueToString( ref nodeInfo, IOUtils.ColorToString( m_outlineColor ) );
  195. IOUtils.AddFieldValueToString( ref nodeInfo, m_mode );
  196. IOUtils.AddFieldValueToString( ref nodeInfo, m_noFog );
  197. }
  198. public void AddToDataCollector( ref MasterNodeDataCollector dataCollector )
  199. {
  200. if( !dataCollector.UsingCustomOutlineColor )
  201. dataCollector.AddToProperties( -1, string.Format( ColorPropertyDec, IOUtils.ColorToString( m_outlineColor ) ), -1 );
  202. if( !dataCollector.UsingCustomOutlineWidth )
  203. dataCollector.AddToProperties( -1, string.Format( OutlinePropertyDec, m_outlineWidth ), -1 );
  204. }
  205. public void UpdateFromMaterial( Material mat )
  206. {
  207. if( mat.HasProperty( ColorPropertyName ) )
  208. {
  209. m_outlineColor = mat.GetColor( ColorPropertyName );
  210. }
  211. if( mat.HasProperty( WidthPropertyName ) )
  212. {
  213. m_outlineWidth = mat.GetFloat( WidthPropertyName );
  214. }
  215. }
  216. void AddMultibodyString( string body , List<string> list )
  217. {
  218. body = body.Replace( "\t\t", string.Empty );
  219. string[] strArr = body.Split( '\n' );
  220. for( int i = 0; i < strArr.Length; i++ )
  221. {
  222. list.Add( strArr[ i ] );
  223. }
  224. }
  225. public string[] OutlineFunctionBody( ref MasterNodeDataCollector dataCollector, bool instanced, bool isShadowCaster, string shaderName, string[] billboardInfo, ref TessellationOpHelper tessOpHelper, string target )
  226. {
  227. List<string> body = new List<string>();
  228. body.Add( ModeTags[ dataCollector.CustomOutlineSelectedAlpha ] );
  229. if( !string.IsNullOrEmpty( m_grabPasses ))
  230. body.Add( m_grabPasses.Replace( "\t\t",string.Empty ));
  231. if( m_zWriteMode != 0 )
  232. body.Add( "ZWrite " + ZBufferOpHelper.ZWriteModeValues[ m_zWriteMode ] );
  233. if( m_zTestMode != 0 )
  234. body.Add( "ZTest " + ZBufferOpHelper.ZTestModeValues[ m_zTestMode ] );
  235. body.Add( "Cull " + m_cullMode );
  236. body.Add( "CGPROGRAM" );
  237. if( tessOpHelper.EnableTesselation )
  238. {
  239. body.Add( "#include \"" + TessellationOpHelper.TessInclude + "\"" );
  240. body.Add( "#pragma target " + target );
  241. }
  242. else
  243. {
  244. body.Add( "#pragma target 3.0" );
  245. }
  246. bool customOutline = dataCollector.UsingCustomOutlineColor || dataCollector.UsingCustomOutlineWidth || dataCollector.UsingCustomOutlineAlpha;
  247. int outlineMode = customOutline ? m_offsetMode : ( m_mode == OutlineMode.VertexOffset ? 0 : 1 );
  248. string extraOptions = ( customOutline ? m_customNoFog : m_noFog ) ? "nofog " : string.Empty;
  249. if( dataCollector.CustomOutlineSelectedAlpha > 0 )
  250. {
  251. extraOptions += ModePragma[ dataCollector.CustomOutlineSelectedAlpha ];
  252. }
  253. string surfConfig = string.Format( OutlineSurfaceConfig, extraOptions );
  254. if( tessOpHelper.EnableTesselation )
  255. tessOpHelper.WriteToOptionalParams( ref surfConfig );
  256. body.Add( surfConfig );
  257. if( !isShadowCaster )
  258. {
  259. AddMultibodyString( m_defines, body );
  260. AddMultibodyString( m_includes, body );
  261. AddMultibodyString( m_pragmas, body );
  262. }
  263. if( instanced )
  264. {
  265. body.Add( OutlineInstancedHeader );
  266. }
  267. if( customOutline )
  268. {
  269. if( isShadowCaster )
  270. {
  271. for( int i = 0; i < InputList.Count; i++ )
  272. {
  273. dataCollector.AddToInput( InputList[ i ].NodeId, InputList[ i ].PropertyName, true );
  274. }
  275. }
  276. else
  277. {
  278. if( !string.IsNullOrEmpty( m_inputs ) )
  279. body.Add( m_inputs.Trim( '\t', '\n' ) );
  280. }
  281. if( !DirtyInput && !isShadowCaster )
  282. body.Add( OutlineBodyStructDefault );
  283. if( !isShadowCaster )
  284. body.Add( OutlineBodyStructEnd );
  285. }
  286. else if( !isShadowCaster )
  287. {
  288. body.Add( OutlineBodyStructBegin );
  289. body.Add( OutlineBodyStructDefault );
  290. body.Add( OutlineBodyStructEnd );
  291. }
  292. if( instanced )
  293. {
  294. for( int i = 0; i < OutlineBodyInstancedBegin.Length; i++ )
  295. {
  296. body.Add( ( i == 0 ) ? string.Format( OutlineBodyInstancedBegin[ i ], shaderName ) : OutlineBodyInstancedBegin[ i ] );
  297. }
  298. if( (object)billboardInfo != null )
  299. {
  300. for( int j = 0; j < billboardInfo.Length; j++ )
  301. {
  302. body.Add( string.Format( BillboardInstructionFormat, billboardInfo[ j ] ) );
  303. }
  304. }
  305. switch( outlineMode )
  306. {
  307. case 0: body.Add( string.Format( OutlineVertexOffsetMode, WidthVariableAccessInstanced ) ); break;
  308. case 1: body.Add( string.Format( OutlineVertexScaleMode, WidthVariableAccessInstanced ) ); break;
  309. case 2: body.Add( string.Format( OutlineVertexCustomMode, WidthVariableAccessInstanced ) ); break;
  310. }
  311. for( int i = 0; i < OutlineBodyInstancedEnd.Length; i++ )
  312. {
  313. body.Add( OutlineBodyInstancedEnd[ i ] );
  314. }
  315. }
  316. else
  317. {
  318. if( customOutline )
  319. {
  320. if( isShadowCaster )
  321. {
  322. for( int i = 0; i < UniformList.Count; i++ )
  323. {
  324. dataCollector.AddToUniforms( UniformList[ i ].NodeId, UniformList[ i ].PropertyName );
  325. }
  326. foreach( KeyValuePair<string, string> kvp in m_localFunctions )
  327. {
  328. dataCollector.AddFunction( kvp.Key, kvp.Value );
  329. }
  330. }
  331. else
  332. {
  333. if( !string.IsNullOrEmpty( Uniforms ) )
  334. body.Add( Uniforms.Trim( '\t', '\n' ) );
  335. }
  336. }
  337. if( !dataCollector.UsingCustomOutlineColor )
  338. body.Add( OutlineDefaultUniformColor );
  339. if( !dataCollector.UsingCustomOutlineWidth )
  340. body.Add( OutlineDefaultUniformWidth );
  341. //Functions
  342. if( customOutline && !isShadowCaster )
  343. body.Add( Functions );
  344. if( tessOpHelper.EnableTesselation && !isShadowCaster )
  345. {
  346. body.Add( tessOpHelper.Uniforms().TrimStart( '\t' ) );
  347. body.Add( tessOpHelper.GetCurrentTessellationFunction.Trim( '\t', '\n' ) + "\n" );
  348. }
  349. if( tessOpHelper.EnableTesselation )
  350. {
  351. body.Add( OutlineTessVertexHeader );
  352. }
  353. else
  354. {
  355. body.Add( OutlineDefaultVertexHeader );
  356. body.Add( OutlineDefaultVertexOutputDeclaration );
  357. }
  358. if( customOutline )
  359. {
  360. if( !string.IsNullOrEmpty( VertexData ) )
  361. body.Add( "\t" + VertexData.Trim( '\t', '\n' ) );
  362. }
  363. if( (object)billboardInfo != null )
  364. {
  365. for( int j = 0; j < billboardInfo.Length; j++ )
  366. {
  367. body.Add( string.Format( BillboardInstructionFormat, billboardInfo[ j ] ) );
  368. }
  369. }
  370. switch( outlineMode )
  371. {
  372. case 0: body.Add( string.Format( OutlineVertexOffsetMode, dataCollector.UsingCustomOutlineWidth ? "outlineVar" : WidthPropertyName ) ); break;
  373. case 1: body.Add( string.Format( OutlineVertexScaleMode, dataCollector.UsingCustomOutlineWidth ? "outlineVar" : WidthPropertyName ) ); break;
  374. case 2: body.Add( string.Format( OutlineVertexCustomMode, dataCollector.UsingCustomOutlineWidth ? "outlineVar" : WidthPropertyName ) ); break;
  375. }
  376. for( int i = 0; i < OutlineBodyDefaultSurfBegin.Length; i++ )
  377. {
  378. body.Add( OutlineBodyDefaultSurfBegin[ i ] );
  379. }
  380. if( dataCollector.UsingCustomOutlineColor || dataCollector.CustomOutlineSelectedAlpha > 0 )
  381. {
  382. body.Add( "\t" + Instructions.Trim( '\t', '\n' ) );
  383. }
  384. else
  385. {
  386. for( int i = 0; i < OutlineSurfBody.Length; i++ )
  387. {
  388. body.Add( OutlineSurfBody[ i ] );
  389. }
  390. }
  391. for( int i = 0; i < OutlineBodyDefaultSurfEnd.Length; i++ )
  392. {
  393. body.Add( OutlineBodyDefaultSurfEnd[ i ] );
  394. }
  395. }
  396. string[] bodyArr = body.ToArray();
  397. body.Clear();
  398. body = null;
  399. return bodyArr;
  400. }
  401. public void Destroy()
  402. {
  403. m_inputList = null;
  404. m_uniformList = null;
  405. m_localFunctions = null;
  406. }
  407. public bool EnableOutline { get { return m_enabled; } }
  408. public bool UsingCullMode { get { return m_cullMode != CullMode.Front; } }
  409. public bool UsingZWrite { get { return m_zWriteMode != 0; } }
  410. public bool UsingZTest { get { return m_zTestMode != 0; } }
  411. public int ZWriteMode { get { return m_zWriteMode; } set { m_zWriteMode = value; } }
  412. public int ZTestMode { get { return m_zTestMode; } set { m_zTestMode = value; } }
  413. public CullMode OutlineCullMode { get { return m_cullMode; } set { m_cullMode = value; } }
  414. public string Inputs { get { return m_inputs; } set { m_inputs = value; } }
  415. public string Uniforms { get { return m_uniforms; } set { m_uniforms = value; } }
  416. public string Instructions { get { return m_instructions; } set { m_instructions = value; } }
  417. public string Functions { get { return m_functions; } set { m_functions = value; } }
  418. public string Includes { get { return m_includes; } set { m_includes = value; } }
  419. public string Pragmas { get { return m_pragmas; } set { m_pragmas = value; } }
  420. public string Defines { get { return m_defines; } set { m_defines = value; } }
  421. public string VertexData { get { return m_vertexData; } set { m_vertexData = value; } }
  422. public string GrabPasses { get { return m_grabPasses; } set { m_grabPasses = value; } }
  423. public List<PropertyDataCollector> InputList { get { return m_inputList; } set { m_inputList = value; } }
  424. public List<PropertyDataCollector> UniformList { get { return m_uniformList; } set { m_uniformList = value; } }
  425. public Dictionary<string, string> LocalFunctions { get { return m_localFunctions; } set { m_localFunctions = value; } }
  426. public bool DirtyInput { get { return m_dirtyInput; } set { m_dirtyInput = value; } }
  427. //public OutlineMode CustomMode { get { return m_customMode; } set { m_customMode = value; } }
  428. public int OffsetMode { get { return m_offsetMode; } set { m_offsetMode = value; } }
  429. public bool CustomNoFog { get { return m_customNoFog; } set { m_customNoFog = value; } }
  430. }
  431. }