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.

241 lines
9.3 KiB

  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[] BillboardCylindricalInstructions = { "//Calculate new billboard vertex position and normal",
  22. "float3 upCamVec = float3( 0, 1, 0 )"};
  23. public static readonly string[] BillboardSphericalInstructions = { "//Calculate new billboard vertex position and normal",
  24. "float3 upCamVec = normalize ( UNITY_MATRIX_V._m10_m11_m12 )"};
  25. public static readonly string[] BillboardCommonInstructions = { "float3 forwardCamVec = -normalize ( UNITY_MATRIX_V._m20_m21_m22 )",
  26. "float3 rightCamVec = normalize( UNITY_MATRIX_V._m00_m01_m02 )",
  27. "float4x4 rotationCamMatrix = float4x4( rightCamVec, 0, upCamVec, 0, forwardCamVec, 0, 0, 0, 0, 1 )",
  28. "{0} = normalize( mul( float4( {0} , 0 ), rotationCamMatrix ))"};
  29. public static readonly string[] BillboardRotDependent = { "//This unfortunately must be made to take non-uniform scaling into account",
  30. "//Transform to world coords, apply rotation and transform back to local",
  31. "{0} = mul( {0} , unity_ObjectToWorld ){1}",
  32. "{0} = mul( {0} , rotationCamMatrix ){1}",
  33. "{0} = mul( {0} , unity_WorldToObject ){1}"};
  34. public static readonly string[] BillboardRotIndependent = { "{0}.x *= length( unity_ObjectToWorld._m00_m10_m20 )",
  35. "{0}.y *= length( unity_ObjectToWorld._m01_m11_m21 )",
  36. "{0}.z *= length( unity_ObjectToWorld._m02_m12_m22 )",
  37. "{0} = mul( {0}, rotationCamMatrix )",
  38. "{0}.xyz += unity_ObjectToWorld._m03_m13_m23",
  39. "//Need to nullify rotation inserted by generated surface shader",
  40. "{0} = mul( unity_WorldToObject, {0} )"};
  41. public static readonly string[] BillboardHDRotDependent = { "//This unfortunately must be made to take non-uniform scaling into account",
  42. "//Transform to world coords, apply rotation and transform back to local",
  43. "{0} = mul( {0} , GetObjectToWorldMatrix() ){1}",
  44. "{0} = mul( {0} , rotationCamMatrix ){1}",
  45. "{0} = mul( {0} , GetWorldToObjectMatrix() ){1}"};
  46. public static readonly string[] BillboardHDRotIndependent = { "{0}.x *= length( GetObjectToWorldMatrix()._m00_m10_m20 )",
  47. "{0}.y *= length( GetObjectToWorldMatrix()._m01_m11_m21 )",
  48. "{0}.z *= length( GetObjectToWorldMatrix()._m02_m12_m22 )",
  49. "{0} = mul( {0}, rotationCamMatrix )",
  50. "{0}.xyz += GetObjectToWorldMatrix()._m03_m13_m23",
  51. "//Need to nullify rotation inserted by generated surface shader",
  52. "{0} = mul( GetWorldToObjectMatrix(), {0} )"};
  53. [SerializeField]
  54. private bool m_isBillboard = false;
  55. [SerializeField]
  56. private BillboardType m_billboardType = BillboardType.Cylindrical;
  57. [SerializeField]
  58. private bool m_rotationIndependent = false;
  59. public void Draw( ParentNode owner )
  60. {
  61. bool visible = owner.ContainerGraph.ParentWindow.InnerWindowVariables.ExpandedVertexOptions;
  62. bool enabled = m_isBillboard;
  63. NodeUtils.DrawPropertyGroup( owner, ref visible, ref m_isBillboard, BillboardTitleStr, () =>
  64. {
  65. m_billboardType = (BillboardType)owner.EditorGUILayoutEnumPopup( BillboardTypeStr, m_billboardType );
  66. m_rotationIndependent = owner.EditorGUILayoutToggle( BillboardRotIndStr, m_rotationIndependent );
  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" , false );
  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, bool vertexIsFloat3 )
  83. {
  84. switch( billboardType )
  85. {
  86. case BillboardType.Cylindrical:
  87. {
  88. for( int i = 0; i < BillboardCylindricalInstructions.Length; i++ )
  89. {
  90. dataCollector.AddVertexInstruction( BillboardCylindricalInstructions[ i ] + ( dataCollector.IsTemplate ? ";" : string.Empty ), -1, true );
  91. }
  92. }
  93. break;
  94. case BillboardType.Spherical:
  95. {
  96. for( int i = 0; i < BillboardCylindricalInstructions.Length; i++ )
  97. {
  98. dataCollector.AddVertexInstruction( BillboardSphericalInstructions[ i ] + ( dataCollector.IsTemplate ? ";" : string.Empty ), -1, true );
  99. }
  100. }
  101. break;
  102. }
  103. for( int i = 0; i < BillboardCommonInstructions.Length; i++ )
  104. {
  105. string value = ( i == 3 ) ? string.Format( BillboardCommonInstructions[ i ], vertexNormalValue ) : BillboardCommonInstructions[ i ];
  106. dataCollector.AddVertexInstruction( value + ( dataCollector.IsTemplate ? ";" : string.Empty ), -1, true );
  107. }
  108. if( rotationIndependent )
  109. {
  110. for( int i = 0; i < BillboardRotIndependent.Length; i++ )
  111. {
  112. string value = string.Empty;
  113. if( dataCollector.IsTemplate && dataCollector.TemplateDataCollectorInstance.CurrentSRPType == TemplateSRPType.HD )
  114. {
  115. value = ( i != 5 ) ? string.Format( BillboardHDRotIndependent[ i ], vertexPosValue ) : BillboardHDRotIndependent[ i ];
  116. }
  117. else
  118. {
  119. value = ( i != 5 ) ? string.Format( BillboardRotIndependent[ i ], vertexPosValue ) : BillboardRotIndependent[ i ];
  120. }
  121. dataCollector.AddVertexInstruction( value + ( dataCollector.IsTemplate ? ";" : string.Empty ), -1, true );
  122. }
  123. }
  124. else
  125. {
  126. for( int i = 0; i < BillboardRotDependent.Length; i++ )
  127. {
  128. string value = string.Empty;
  129. if( dataCollector.IsTemplate && dataCollector.TemplateDataCollectorInstance.CurrentSRPType == TemplateSRPType.HD )
  130. {
  131. value = ( i > 1 ) ? string.Format( BillboardHDRotDependent[ i ], vertexPosValue, ( vertexIsFloat3 ? ".xyz" : string.Empty ) ) : BillboardHDRotDependent[ i ];
  132. }
  133. else
  134. {
  135. value = ( i > 1 ) ? string.Format( BillboardRotDependent[ i ], vertexPosValue, ( vertexIsFloat3 ? ".xyz" : string.Empty ) ) : BillboardRotDependent[ i ];
  136. }
  137. dataCollector.AddVertexInstruction( value + ( dataCollector.IsTemplate ? ";" : string.Empty ), -1, true );
  138. }
  139. }
  140. }
  141. public string[] GetInternalMultilineInstructions()
  142. {
  143. // This method is only used on Surface ... no HD variation is needed
  144. return GetMultilineInstructions( m_billboardType, m_rotationIndependent, "v.vertex", "v.normal" );
  145. }
  146. public static string[] GetMultilineInstructions( BillboardType billboardType, bool rotationIndependent, string vertexPosValue, string vertexNormalValue )
  147. {
  148. // This method is only used on Surface ... no HD variation is needed
  149. List<string> body = new List<string>();
  150. switch( billboardType )
  151. {
  152. case BillboardType.Cylindrical:
  153. {
  154. for( int i = 0; i < BillboardCylindricalInstructions.Length; i++ )
  155. {
  156. body.Add( BillboardCylindricalInstructions[ i ] );
  157. }
  158. }
  159. break;
  160. case BillboardType.Spherical:
  161. {
  162. for( int i = 0; i < BillboardCylindricalInstructions.Length; i++ )
  163. {
  164. body.Add( BillboardSphericalInstructions[ i ] );
  165. }
  166. }
  167. break;
  168. }
  169. for( int i = 0; i < BillboardCommonInstructions.Length; i++ )
  170. {
  171. string value = ( i == 3 ) ? string.Format( BillboardCommonInstructions[ i ], vertexNormalValue ) : BillboardCommonInstructions[ i ];
  172. body.Add( value );
  173. }
  174. if( rotationIndependent )
  175. {
  176. for( int i = 0; i < BillboardRotIndependent.Length; i++ )
  177. {
  178. string value = ( i != 5 ) ? string.Format( BillboardRotIndependent[ i ], vertexPosValue ) : BillboardRotIndependent[ i ];
  179. body.Add( value );
  180. }
  181. }
  182. else
  183. {
  184. for( int i = 0; i < BillboardRotDependent.Length; i++ )
  185. {
  186. string value = ( i > 1 ) ? string.Format( BillboardRotDependent[ i ], vertexPosValue ) : BillboardRotDependent[ i ];
  187. body.Add( value );
  188. }
  189. }
  190. return body.ToArray();
  191. }
  192. public void ReadFromString( ref uint index, ref string[] nodeParams )
  193. {
  194. m_isBillboard = Convert.ToBoolean( nodeParams[ index++ ] );
  195. m_billboardType = (BillboardType)Enum.Parse( typeof( BillboardType ), nodeParams[ index++ ] );
  196. if( UIUtils.CurrentShaderVersion() > 11007 )
  197. {
  198. m_rotationIndependent = Convert.ToBoolean( nodeParams[ index++ ] );
  199. }
  200. }
  201. public void WriteToString( ref string nodeInfo )
  202. {
  203. IOUtils.AddFieldValueToString( ref nodeInfo, m_isBillboard );
  204. IOUtils.AddFieldValueToString( ref nodeInfo, m_billboardType );
  205. IOUtils.AddFieldValueToString( ref nodeInfo, m_rotationIndependent );
  206. }
  207. public bool IsBillboard { get { return m_isBillboard; } }
  208. }
  209. }