SpeedTree8MaterialUpgrader.cs 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  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. int cullmode = 0;
  46. mat.SetFloat("_WINDQUALITY", windQuality);
  47. if (isBillboard)
  48. {
  49. mat.EnableKeyword("EFFECT_BILLBOARD");
  50. cullmode = 2;
  51. }
  52. if (mat.HasProperty("_CullMode"))
  53. mat.SetFloat("_CullMode", cullmode);
  54. }
  55. /// <summary>
  56. /// Postprocesses materials while you are importing a SpeedTree 8 asset. Call from OnPostprocessSpeedTree in a MaterialPostprocessor.
  57. /// </summary>
  58. /// <param name="speedtree">The GameObject Unity creates from this imported SpeedTree.</param>
  59. /// <param name="stImporter">The asset importer used to import this SpeedTree asset.</param>
  60. /// <param name="finalizer">Render pipeline-specific material finalizer.</param>
  61. public static void PostprocessSpeedTree8Materials(GameObject speedtree, SpeedTreeImporter stImporter, MaterialFinalizer finalizer = null)
  62. {
  63. LODGroup lg = speedtree.GetComponent<LODGroup>();
  64. LOD[] lods = lg.GetLODs();
  65. for (int l = 0; l < lods.Length; l++)
  66. {
  67. LOD lod = lods[l];
  68. bool isBillboard = stImporter.hasBillboard && (l == lods.Length - 1);
  69. int wq = Mathf.Min(stImporter.windQualities[l], stImporter.bestWindQuality);
  70. foreach (Renderer r in lod.renderers)
  71. {
  72. // Override default motion vector generation mode pending
  73. // proper motion vector integration in SRPs.
  74. r.motionVectorGenerationMode = MotionVectorGenerationMode.Camera;
  75. foreach (Material m in r.sharedMaterials)
  76. {
  77. ImportNewSpeedTree8Material(m, wq, isBillboard);
  78. if (finalizer != null)
  79. finalizer(m);
  80. }
  81. }
  82. }
  83. }
  84. /// <summary>
  85. /// Preserves wind quality and billboard settings while you are upgrading a SpeedTree 8 material from previous versions of SpeedTree 8.
  86. /// To determine which WindQuality to use, Unity checks the keywords first and then the _WindQuality float value.
  87. /// See SpeedTree in the Unity Manual for the values associated with different WindQuality settings.
  88. /// Should work for upgrading versions within a pipeline and from standard to current pipeline.
  89. /// </summary>
  90. /// <param name="material">SpeedTree 8 material to upgrade.</param>
  91. public static void SpeedTree8MaterialFinalizer(Material material)
  92. {
  93. if (material.HasProperty("_TwoSided") && material.HasProperty("_CullMode"))
  94. material.SetFloat("_CullMode", material.GetFloat("_TwoSided"));
  95. if (material.IsKeywordEnabled("EFFECT_EXTRA_TEX"))
  96. material.SetFloat("EFFECT_EXTRA_TEX", 1.0f);
  97. bool isBillboard = material.IsKeywordEnabled("EFFECT_BILLBOARD");
  98. if (material.HasProperty("EFFECT_BILLBOARD"))
  99. material.SetFloat("EFFECT_BILLBOARD", isBillboard ? 1.0f : 0.0f);
  100. UpgradeWindQuality(material);
  101. }
  102. private static void UpgradeWindQuality(Material material, int windQuality = -1)
  103. {
  104. int wq = GetWindQuality(material, windQuality);
  105. SetWindQuality(material, wq);
  106. }
  107. private static int GetWindQuality(Material material, int windQuality = -1)
  108. {
  109. // Conservative wind quality priority:
  110. // input WindQuality > enabled keyword > _WindQuality float value
  111. if (!WindIntValid(windQuality))
  112. {
  113. windQuality = GetWindQualityFromKeywords(material.shaderKeywords);
  114. if (!WindIntValid(windQuality))
  115. {
  116. windQuality = material.HasProperty("_WindQuality") ? (int)material.GetFloat("_WindQuality") : 0;
  117. if (!WindIntValid(windQuality))
  118. windQuality = 0;
  119. }
  120. }
  121. return windQuality;
  122. }
  123. private static void ClearWindKeywords(Material material)
  124. {
  125. if (material == null)
  126. return;
  127. for (int i = 0; i < (int)WindQuality.Count; i++)
  128. {
  129. material.DisableKeyword(WindQualityString[i]);
  130. }
  131. }
  132. private static void SetWindQuality(Material material, int windQuality)
  133. {
  134. Debug.Assert(WindIntValid(windQuality), "Attempting to set invalid wind quality on material " + material.name);
  135. if (material == null)
  136. return;
  137. if (windQuality != GetWindQualityFromKeywords(material.shaderKeywords))
  138. {
  139. ClearWindKeywords(material);
  140. }
  141. material.EnableKeyword(WindQualityString[windQuality]);
  142. material.SetFloat("_WindQuality", windQuality); // A legacy float used in native code to apply wind data
  143. if (material.HasProperty("_WINDQUALITY"))
  144. material.SetFloat("_WINDQUALITY", windQuality); // The actual name of the keyword enum for the shadergraph
  145. }
  146. private static int GetWindQualityFromKeywords(string[] matKws)
  147. {
  148. foreach (string kw in matKws)
  149. {
  150. if (kw.StartsWith("_WINDQUALITY_"))
  151. {
  152. for (int i = 0; i < (int)WindQuality.Count; i++)
  153. {
  154. if (kw.EndsWith(WindQualityString[i]))
  155. return i;
  156. }
  157. }
  158. }
  159. return -1;
  160. }
  161. private static bool WindIntValid(int windInt)
  162. {
  163. return ((int)WindQuality.None <= windInt) && (windInt < (int)WindQuality.Count);
  164. }
  165. }
  166. }