SpeedTree8MaterialUpgrader.cs 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. using System.Collections.Generic;
  2. using UnityEngine;
  3. using System;
  4. namespace UnityEditor.Rendering
  5. {
  6. /// <summary>
  7. /// Material upgrader and relevant utilities for SpeedTree 8.
  8. /// </summary>
  9. public class SpeedTree8MaterialUpgrader : MaterialUpgrader
  10. {
  11. private enum WindQuality
  12. {
  13. None = 0,
  14. Fastest,
  15. Fast,
  16. Better,
  17. Best,
  18. Palm,
  19. Count
  20. }
  21. private static string[] WindQualityString =
  22. {
  23. "_WINDQUALITY_NONE",
  24. "_WINDQUALITY_FASTEST",
  25. "_WINDQUALITY_FAST",
  26. "_WINDQUALITY_BETTER",
  27. "_WINDQUALITY_BEST",
  28. "_WINDQUALITY_PALM"
  29. };
  30. /// <summary>
  31. /// Creates a material upgrader that handles the property renames that HD and Universal have in common when upgrading
  32. /// from the built-in SpeedTree 8 shader.
  33. /// </summary>
  34. /// <param name="sourceShaderName">Original SpeedTree8 shader name.</param>
  35. /// <param name="destShaderName">New SpeedTree 8 shader name.</param>
  36. /// <param name="finalizer">A delegate that postprocesses the material for the render pipeline in use.</param>
  37. public SpeedTree8MaterialUpgrader(string sourceShaderName, string destShaderName, MaterialFinalizer finalizer = null)
  38. {
  39. RenameShader(sourceShaderName, destShaderName, finalizer);
  40. RenameFloat("_WindQuality", "_WINDQUALITY");
  41. RenameFloat("_TwoSided", "_CullMode"); // Currently only used in HD. Update this once URP per-material cullmode is enabled via shadergraph.
  42. }
  43. private static void ImportNewSpeedTree8Material(Material mat, int windQuality, bool isBillboard)
  44. {
  45. if (mat == null)
  46. return;
  47. int cullmode = 0;
  48. mat.SetFloat("_WINDQUALITY", windQuality);
  49. if (isBillboard)
  50. {
  51. mat.SetFloat("EFFECT_BILLBOARD", 1.0f);
  52. cullmode = 2;
  53. }
  54. if (mat.HasProperty("_CullMode"))
  55. mat.SetFloat("_CullMode", cullmode);
  56. if (mat.IsKeywordEnabled("EFFECT_EXTRA_TEX"))
  57. mat.SetFloat("EFFECT_EXTRA_TEX", 1.0f);
  58. }
  59. /// <summary>
  60. /// Postprocesses materials while you are importing a SpeedTree 8 asset. Call from OnPostprocessSpeedTree in a MaterialPostprocessor.
  61. /// </summary>
  62. /// <param name="speedtree">The GameObject Unity creates from this imported SpeedTree.</param>
  63. /// <param name="stImporter">The asset importer used to import this SpeedTree asset.</param>
  64. /// <param name="finalizer">Render pipeline-specific material finalizer.</param>
  65. public static void PostprocessSpeedTree8Materials(GameObject speedtree, SpeedTreeImporter stImporter, MaterialFinalizer finalizer = null)
  66. {
  67. LODGroup lg = speedtree.GetComponent<LODGroup>();
  68. LOD[] lods = lg.GetLODs();
  69. for (int l = 0; l < lods.Length; l++)
  70. {
  71. LOD lod = lods[l];
  72. bool isBillboard = stImporter.hasBillboard && (l == lods.Length - 1);
  73. int wq = Mathf.Min(stImporter.windQualities[l], stImporter.bestWindQuality);
  74. foreach (Renderer r in lod.renderers)
  75. {
  76. // Override default motion vector generation mode pending
  77. // proper motion vector integration in SRPs.
  78. r.motionVectorGenerationMode = MotionVectorGenerationMode.Camera;
  79. foreach (Material m in r.sharedMaterials)
  80. {
  81. float cutoff = stImporter.alphaTestRef;
  82. ImportNewSpeedTree8Material(m, wq, isBillboard);
  83. if (finalizer != null)
  84. finalizer(m);
  85. }
  86. }
  87. }
  88. }
  89. /// <summary>
  90. /// Preserves wind quality and billboard settings while you are upgrading a SpeedTree 8 material from previous versions of SpeedTree 8.
  91. /// Wind priority order is enabled keyword > _WindQuality float value.
  92. /// Should work for upgrading versions within a pipeline and from standard to current pipeline.
  93. /// </summary>
  94. /// <param name="material">SpeedTree 8 material to upgrade.</param>
  95. public static void SpeedTree8MaterialFinalizer(Material material)
  96. {
  97. if (material.HasProperty("_TwoSided") && material.HasProperty("_CullMode"))
  98. material.SetFloat("_CullMode", material.GetFloat("_TwoSided"));
  99. if (material.IsKeywordEnabled("EFFECT_EXTRA_TEX"))
  100. material.SetFloat("EFFECT_EXTRA_TEX", 1.0f);
  101. bool isBillboard = material.IsKeywordEnabled("EFFECT_BILLBOARD");
  102. if (material.HasProperty("EFFECT_BILLBOARD"))
  103. material.SetFloat("EFFECT_BILLBOARD", isBillboard ? 1.0f : 0.0f);
  104. UpgradeWindQuality(material);
  105. }
  106. private static void UpgradeWindQuality(Material material, int windQuality = -1)
  107. {
  108. int wq = GetWindQuality(material, windQuality);
  109. SetWindQuality(material, wq);
  110. }
  111. private static int GetWindQuality(Material material, int windQuality = -1)
  112. {
  113. // Conservative wind quality priority:
  114. // input WindQuality > enabled keyword > _WindQuality float value
  115. if (!WindIntValid(windQuality))
  116. {
  117. windQuality = GetWindQualityFromKeywords(material.shaderKeywords);
  118. if (!WindIntValid(windQuality))
  119. {
  120. windQuality = material.HasProperty("_WindQuality") ? (int)material.GetFloat("_WindQuality") : 0;
  121. if (!WindIntValid(windQuality))
  122. windQuality = 0;
  123. }
  124. }
  125. return windQuality;
  126. }
  127. private static void ClearWindKeywords(Material material)
  128. {
  129. if (material == null)
  130. return;
  131. for (int i = 0; i < (int)WindQuality.Count; i++)
  132. {
  133. material.DisableKeyword(WindQualityString[i]);
  134. }
  135. }
  136. private static void SetWindQuality(Material material, int windQuality)
  137. {
  138. Debug.Assert(WindIntValid(windQuality), "Attempting to set invalid wind quality on material " + material.name);
  139. if (material == null)
  140. return;
  141. if (windQuality != GetWindQualityFromKeywords(material.shaderKeywords))
  142. {
  143. ClearWindKeywords(material);
  144. }
  145. material.EnableKeyword(WindQualityString[windQuality]);
  146. material.SetFloat("_WindQuality", windQuality); // A legacy float used in native code to apply wind data
  147. if (material.HasProperty("_WINDQUALITY"))
  148. material.SetFloat("_WINDQUALITY", windQuality); // The actual name of the keyword enum for the shadergraph
  149. }
  150. private static int GetWindQualityFromKeywords(string[] matKws)
  151. {
  152. foreach (string kw in matKws)
  153. {
  154. if (kw.StartsWith("_WINDQUALITY_"))
  155. {
  156. for (int i = 0; i < (int)WindQuality.Count; i++)
  157. {
  158. if (kw.EndsWith(WindQualityString[i]))
  159. return i;
  160. }
  161. }
  162. }
  163. return -1;
  164. }
  165. private static bool WindIntValid(int windInt)
  166. {
  167. return ((int)WindQuality.None <= windInt) && (windInt < (int)WindQuality.Count);
  168. }
  169. }
  170. }