BillboardOpHelper.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. // Amplify Shader Editor - Visual Shader Editing Tool
  2. // Copyright (c) Amplify Creations, Lda <info@amplify.pt>
  3. // Billboard based on:
  4. // https://gist.github.com/renaudbedard/7a90ec4a5a7359712202
  5. using System;
  6. using UnityEngine;
  7. using System.Collections.Generic;
  8. namespace AmplifyShaderEditor
  9. {
  10. public enum BillboardType
  11. {
  12. Cylindrical,
  13. Spherical
  14. }
  15. [Serializable]
  16. public class BillboardOpHelper
  17. {
  18. public static readonly string BillboardTitleStr = " Billboard";
  19. public static readonly string BillboardTypeStr = "Type";
  20. public static readonly string BillboardRotIndStr = "Ignore Rotation";
  21. public static readonly string BillboardAffectNormalTangentStr = "Affect Normal/Tangent";
  22. public static readonly string[] BillboardCylindricalInstructions = { "//Calculate new billboard vertex position and normal",
  23. "float3 upCamVec = float3( 0, 1, 0 )"};
  24. public static readonly string[] BillboardSphericalInstructions = { "//Calculate new billboard vertex position and normal",
  25. "float3 upCamVec = normalize ( UNITY_MATRIX_V._m10_m11_m12 )"};
  26. public static readonly string[] BillboardCommonInstructions = { "float3 forwardCamVec = -normalize ( UNITY_MATRIX_V._m20_m21_m22 )",
  27. "float3 rightCamVec = normalize( UNITY_MATRIX_V._m00_m01_m02 )",
  28. "float4x4 rotationCamMatrix = float4x4( rightCamVec, 0, upCamVec, 0, forwardCamVec, 0, 0, 0, 0, 1 )",
  29. "{0} = normalize( mul( float4( {0} , 0 ), rotationCamMatrix )).xyz"};
  30. public static readonly string[] BillboardRotDependent = { "//This unfortunately must be made to take non-uniform scaling into account",
  31. "//Transform to world coords, apply rotation and transform back to local",
  32. "{0} = mul( {1} , unity_ObjectToWorld ){2}",
  33. "{0} = mul( {1} , rotationCamMatrix ){2}",
  34. "{0} = mul( {1} , unity_WorldToObject ){2}"};
  35. public static readonly string[] BillboardRotIndependent = { "{0}.x *= length( unity_ObjectToWorld._m00_m10_m20 )",
  36. "{0}.y *= length( unity_ObjectToWorld._m01_m11_m21 )",
  37. "{0}.z *= length( unity_ObjectToWorld._m02_m12_m22 )",
  38. "{0} = mul( {0}, rotationCamMatrix )",
  39. "{0} = mul( unity_WorldToObject, float4( {0}.xyz, 0 ) )"};
  40. public static readonly string[] BillboardHDRotDependent = { "//This unfortunately must be made to take non-uniform scaling into account",
  41. "//Transform to world coords, apply rotation and transform back to local",
  42. "{0} = mul( {1} , GetObjectToWorldMatrix() ){2}",
  43. "{0} = mul( {1} , rotationCamMatrix ){2}",
  44. "{0} = mul( {1} , GetWorldToObjectMatrix() ){2}"};
  45. public static readonly string[] BillboardHDRotIndependent = { "{0}.x *= length( GetObjectToWorldMatrix()._m00_m10_m20 )",
  46. "{0}.y *= length( GetObjectToWorldMatrix()._m01_m11_m21 )",
  47. "{0}.z *= length( GetObjectToWorldMatrix()._m02_m12_m22 )",
  48. "{0} = mul( {0}, rotationCamMatrix )",
  49. "{0} = mul( GetWorldToObjectMatrix(), float4( {0}.xyz, 0 ) )"};
  50. [SerializeField]
  51. private bool m_isBillboard = false;
  52. [SerializeField]
  53. private BillboardType m_billboardType = BillboardType.Cylindrical;
  54. [SerializeField]
  55. private bool m_rotationIndependent = false;
  56. [SerializeField]
  57. private bool m_affectNormalTangent = true;
  58. public void Draw( ParentNode owner )
  59. {
  60. bool visible = owner.ContainerGraph.ParentWindow.InnerWindowVariables.ExpandedVertexOptions;
  61. bool enabled = m_isBillboard;
  62. NodeUtils.DrawPropertyGroup( owner, ref visible, ref m_isBillboard, BillboardTitleStr, () =>
  63. {
  64. m_billboardType = (BillboardType)owner.EditorGUILayoutEnumPopup( BillboardTypeStr, m_billboardType );
  65. m_rotationIndependent = owner.EditorGUILayoutToggle( BillboardRotIndStr, m_rotationIndependent );
  66. m_affectNormalTangent = owner.EditorGUILayoutToggle( BillboardAffectNormalTangentStr , m_affectNormalTangent );
  67. } );
  68. owner.ContainerGraph.ParentWindow.InnerWindowVariables.ExpandedVertexOptions = visible;
  69. if( m_isBillboard != enabled )
  70. {
  71. UIUtils.RequestSave();
  72. }
  73. }
  74. public void FillDataCollectorWithInternalData( ref MasterNodeDataCollector dataCollector )
  75. {
  76. if( m_isBillboard )
  77. {
  78. FillDataCollector( ref dataCollector, m_billboardType, m_rotationIndependent, "v.vertex", "v.normal","v.tangent", false, m_affectNormalTangent );
  79. }
  80. }
  81. // This should be called after the Vertex Offset and Vertex Normal ports are analised
  82. public static void FillDataCollector( ref MasterNodeDataCollector dataCollector, BillboardType billboardType, bool rotationIndependent, string vertexPosValue, string vertexNormalValue,string vertexTangentValue, bool vertexIsFloat3, bool affectNormalTangent )
  83. {
  84. vertexTangentValue = vertexTangentValue + ".xyz";
  85. switch( billboardType )
  86. {
  87. case BillboardType.Cylindrical:
  88. {
  89. for( int i = 0; i < BillboardCylindricalInstructions.Length; i++ )
  90. {
  91. dataCollector.AddVertexInstruction( BillboardCylindricalInstructions[ i ] + ( dataCollector.IsTemplate ? ";" : string.Empty ), -1, true );
  92. }
  93. }
  94. break;
  95. case BillboardType.Spherical:
  96. {
  97. for( int i = 0; i < BillboardCylindricalInstructions.Length; i++ )
  98. {
  99. dataCollector.AddVertexInstruction( BillboardSphericalInstructions[ i ] + ( dataCollector.IsTemplate ? ";" : string.Empty ), -1, true );
  100. }
  101. }
  102. break;
  103. }
  104. for( int i = 0; i < 3; i++ )
  105. {
  106. dataCollector.AddVertexInstruction( BillboardCommonInstructions[ i ] + ( dataCollector.IsTemplate ? ";" : string.Empty ), -1, true );
  107. }
  108. if( affectNormalTangent )
  109. {
  110. string normalValue = string.Format( BillboardCommonInstructions[ 3 ] , vertexNormalValue );
  111. dataCollector.AddVertexInstruction( normalValue + ( dataCollector.IsTemplate ? ";" : string.Empty ) , -1 , true );
  112. string tangentValue = string.Format( BillboardCommonInstructions[ 3 ] , vertexTangentValue );
  113. dataCollector.AddVertexInstruction( tangentValue + ( dataCollector.IsTemplate ? ";" : string.Empty ) , -1 , true );
  114. }
  115. if( rotationIndependent )
  116. {
  117. for( int i = 0; i < BillboardRotIndependent.Length; i++ )
  118. {
  119. string value = string.Empty;
  120. if( dataCollector.IsTemplate && dataCollector.TemplateDataCollectorInstance.CurrentSRPType != TemplateSRPType.BiRP )
  121. {
  122. value = string.Format( BillboardHDRotIndependent[ i ], vertexPosValue );
  123. }
  124. else
  125. {
  126. value = string.Format( BillboardRotIndependent[ i ], vertexPosValue );
  127. }
  128. dataCollector.AddVertexInstruction( value + ( dataCollector.IsTemplate ? ";" : string.Empty ), -1, true );
  129. }
  130. }
  131. else
  132. {
  133. string vertexPosConverted = vertexIsFloat3 ? string.Format( "float4({0},0)", vertexPosValue ) : vertexPosValue;
  134. for( int i = 0; i < BillboardRotDependent.Length; i++ )
  135. {
  136. string value = string.Empty;
  137. if( dataCollector.IsTemplate && dataCollector.TemplateDataCollectorInstance.CurrentSRPType == TemplateSRPType.HDRP )
  138. {
  139. value = ( i > 1 ) ? string.Format( BillboardHDRotDependent[ i ], vertexPosValue, vertexPosConverted, ( vertexIsFloat3 ? ".xyz" : string.Empty ) ) : BillboardHDRotDependent[ i ];
  140. }
  141. else
  142. {
  143. value = ( i > 1 ) ? string.Format( BillboardRotDependent[ i ], vertexPosValue, vertexPosConverted, ( vertexIsFloat3 ? ".xyz" : string.Empty ) ) : BillboardRotDependent[ i ];
  144. }
  145. dataCollector.AddVertexInstruction( value + ( dataCollector.IsTemplate ? ";" : string.Empty ), -1, true );
  146. }
  147. }
  148. }
  149. public string[] GetInternalMultilineInstructions()
  150. {
  151. // This method is only used on Surface ... no HD variation is needed
  152. return GetMultilineInstructions( m_billboardType, m_rotationIndependent, "v.vertex", "v.normal", "v.tangent",m_affectNormalTangent );
  153. }
  154. public static string[] GetMultilineInstructions( BillboardType billboardType, bool rotationIndependent, string vertexPosValue, string vertexNormalValue, string vertexTangentValue, bool affectNormalTangent )
  155. {
  156. vertexTangentValue += ".xyz";
  157. // This method is only used on Surface ... no HD variation is needed
  158. List<string> body = new List<string>();
  159. switch( billboardType )
  160. {
  161. case BillboardType.Cylindrical:
  162. {
  163. for( int i = 0; i < BillboardCylindricalInstructions.Length; i++ )
  164. {
  165. body.Add( BillboardCylindricalInstructions[ i ] );
  166. }
  167. }
  168. break;
  169. case BillboardType.Spherical:
  170. {
  171. for( int i = 0; i < BillboardCylindricalInstructions.Length; i++ )
  172. {
  173. body.Add( BillboardSphericalInstructions[ i ] );
  174. }
  175. }
  176. break;
  177. }
  178. for( int i = 0; i < 3; i++ )
  179. {
  180. body.Add( BillboardCommonInstructions[ i ] );
  181. }
  182. if( affectNormalTangent )
  183. {
  184. string normalValue = string.Format( BillboardCommonInstructions[ 3 ] , vertexNormalValue );
  185. body.Add( normalValue );
  186. string tangentValue = string.Format( BillboardCommonInstructions[ 3 ] , vertexTangentValue );
  187. body.Add( tangentValue );
  188. }
  189. if( rotationIndependent )
  190. {
  191. for( int i = 0; i < BillboardRotIndependent.Length; i++ )
  192. {
  193. string value = ( i != 5 ) ? string.Format( BillboardRotIndependent[ i ], vertexPosValue ) : BillboardRotIndependent[ i ];
  194. body.Add( value );
  195. }
  196. }
  197. else
  198. {
  199. for( int i = 0; i < BillboardRotDependent.Length; i++ )
  200. {
  201. string value = ( i > 1 ) ? string.Format( BillboardRotDependent[ i ], vertexPosValue ) : BillboardRotDependent[ i ];
  202. body.Add( value );
  203. }
  204. }
  205. return body.ToArray();
  206. }
  207. public void ReadFromString( ref uint index, ref string[] nodeParams )
  208. {
  209. m_isBillboard = Convert.ToBoolean( nodeParams[ index++ ] );
  210. m_billboardType = (BillboardType)Enum.Parse( typeof( BillboardType ), nodeParams[ index++ ] );
  211. if( UIUtils.CurrentShaderVersion() > 11007 )
  212. {
  213. m_rotationIndependent = Convert.ToBoolean( nodeParams[ index++ ] );
  214. }
  215. if( UIUtils.CurrentShaderVersion() > 18918 )
  216. {
  217. m_affectNormalTangent = Convert.ToBoolean( nodeParams[ index++ ] );
  218. }
  219. }
  220. public void WriteToString( ref string nodeInfo )
  221. {
  222. IOUtils.AddFieldValueToString( ref nodeInfo, m_isBillboard );
  223. IOUtils.AddFieldValueToString( ref nodeInfo, m_billboardType );
  224. IOUtils.AddFieldValueToString( ref nodeInfo, m_rotationIndependent );
  225. IOUtils.AddFieldValueToString( ref nodeInfo , m_affectNormalTangent );
  226. }
  227. public bool IsBillboard { get { return m_isBillboard; } }
  228. }
  229. }