A game about forced loneliness, made by TACStudios
at master 220 lines 9.5 kB view raw
1using System.Collections.Generic; 2using UnityEngine; 3using System; 4using UnityEngine.Experimental.Rendering; 5 6namespace UnityEditor.Rendering 7{ 8 /// <summary> 9 /// Material upgrader and relevant utilities for SpeedTree 8. 10 /// </summary> 11 public class SpeedTree8MaterialUpgrader : MaterialUpgrader 12 { 13 private enum WindQuality 14 { 15 None = 0, 16 Fastest, 17 Fast, 18 Better, 19 Best, 20 Palm, 21 Count 22 } 23 24 private static string[] WindQualityString = 25 { 26 "_WINDQUALITY_NONE", 27 "_WINDQUALITY_FASTEST", 28 "_WINDQUALITY_FAST", 29 "_WINDQUALITY_BETTER", 30 "_WINDQUALITY_BEST", 31 "_WINDQUALITY_PALM" 32 }; 33 34 static private class Uniforms 35 { 36 internal static int _WINDQUALITY = Shader.PropertyToID("_WINDQUALITY"); 37 internal static int EFFECT_BILLBOARD = Shader.PropertyToID("EFFECT_BILLBOARD"); 38 internal static int EFFECT_EXTRA_TEX = Shader.PropertyToID("EFFECT_EXTRA_TEX"); 39 internal static int _TwoSided = Shader.PropertyToID("_TwoSided"); 40 internal static int _WindQuality = Shader.PropertyToID("_WindQuality"); 41 } 42 /// <summary> 43 /// Returns true if the material contains a SpeedTree Wind keyword. 44 /// </summary> 45 /// <param name="material">Material to check</param> 46 /// <returns> true if the material has a SpeedTree wind keyword that enables Vertex Shader wind animation </returns> 47 public static bool DoesMaterialHaveSpeedTreeWindKeyword(Material material) 48 { 49 foreach(string keyword in WindQualityString) 50 if(material.IsKeywordEnabled(keyword)) 51 return true; 52 return false; 53 } 54 55 /// <summary> 56 /// Checks the material for SpeedTree keywords to determine if the wind is enabled. 57 /// </summary> 58 /// <param name="material">Material to check</param> 59 /// <returns> true if the material has a SpeedTree wind keyword that enables Vertex Shader wind animation and WindQuality other than None (0) </returns> 60 public static bool IsWindEnabled(Material material) 61 { 62 return HasWindEnabledKeyword(material) && HasWindQualityPropertyEnabled(material); 63 } 64 private static bool HasWindEnabledKeyword(Material material) 65 { 66 for(int i=1/*skip NONE*/; i<WindQualityString.Length; ++i) 67 { 68 if(material.IsKeywordEnabled(WindQualityString[i])) 69 return true; 70 } 71 return false; 72 } 73 private static bool HasWindQualityPropertyEnabled(Material material) 74 { 75 return material.HasProperty("_WindQuality") && material.GetFloat(Uniforms._WindQuality) > 0.0f; 76 } 77 78 /// <summary> 79 /// Creates a material upgrader that handles the property renames that HD and Universal have in common when upgrading 80 /// from the built-in SpeedTree 8 shader. 81 /// </summary> 82 /// <param name="sourceShaderName">Original SpeedTree8 shader name.</param> 83 /// <param name="destShaderName">New SpeedTree 8 shader name.</param> 84 /// <param name="finalizer">A delegate that postprocesses the material for the render pipeline in use.</param> 85 public SpeedTree8MaterialUpgrader(string sourceShaderName, string destShaderName, MaterialFinalizer finalizer = null) 86 { 87 RenameShader(sourceShaderName, destShaderName, finalizer); 88 RenameFloat("_WindQuality", "_WINDQUALITY"); 89 RenameFloat("_BillboardKwToggle", "EFFECT_BILLBOARD"); 90 RenameKeywordToFloat("EFFECT_EXTRA_TEX", "EFFECT_EXTRA_TEX", 1, 0); 91 RenameKeywordToFloat("EFFECT_SUBSURFACE", "_SubsurfaceKwToggle", 1, 0); 92 RenameKeywordToFloat("EFFECT_BUMP", "_NormalMapKwToggle", 1, 0); 93 RenameKeywordToFloat("EFFECT_HUE_VARIATION", "_HueVariationKwToggle", 1, 0); 94 } 95 96 /// <summary> 97 /// Postprocesses materials while you are importing a SpeedTree 8 asset. Call from OnPostprocessSpeedTree in a MaterialPostprocessor. 98 /// </summary> 99 /// <param name="speedtree">The GameObject Unity creates from this imported SpeedTree.</param> 100 /// <param name="stImporter">The asset importer used to import this SpeedTree asset.</param> 101 /// <param name="finalizer">Render pipeline-specific material finalizer.</param> 102 public static void PostprocessSpeedTree8Materials(GameObject speedtree, SpeedTreeImporter stImporter, MaterialFinalizer finalizer = null) 103 { 104 LODGroup lg = speedtree.GetComponent<LODGroup>(); 105 LOD[] lods = lg.GetLODs(); 106 for (int l = 0; l < lods.Length; l++) 107 { 108 LOD lod = lods[l]; 109 bool isBillboard = stImporter.hasBillboard && (l == lods.Length - 1); 110 int wq = Mathf.Min(stImporter.windQualities[l], stImporter.bestWindQuality); 111 foreach (Renderer r in lod.renderers) 112 { 113 foreach (Material m in r.sharedMaterials) 114 { 115 if (m == null) 116 continue; 117 118 float cutoff = stImporter.alphaTestRef; 119 int cullmode = isBillboard ? 2 : 0; 120 121 m.SetFloat(Uniforms._WINDQUALITY, wq); 122 if (isBillboard) 123 { 124 m.SetFloat(Uniforms.EFFECT_BILLBOARD, 1.0f); 125 } 126 m.SetFloat(Uniforms._TwoSided, cullmode); // Temporary; Finalizer should read from this and apply the value to a pipeline-specific cull property 127 if (m.IsKeywordEnabled("EFFECT_EXTRA_TEX")) 128 m.SetFloat(Uniforms.EFFECT_EXTRA_TEX, 1.0f); 129 130 if (finalizer != null) 131 finalizer(m); 132 } 133 } 134 } 135 } 136 137 /// <summary> 138 /// Preserves wind quality and billboard settings while you are upgrading a SpeedTree 8 material from previous versions of SpeedTree 8. 139 /// Wind priority order is _WindQuality float value > enabled keyword. 140 /// Should work for upgrading versions within a pipeline and from standard to current pipeline. 141 /// </summary> 142 /// <param name="material">SpeedTree 8 material to upgrade.</param> 143 public static void SpeedTree8MaterialFinalizer(Material material) 144 { 145 UpgradeWindQuality(material); 146 } 147 148 private static void UpgradeWindQuality(Material material, int windQuality = -1) 149 { 150 int wq = GetWindQuality(material, windQuality); 151 SetWindQuality(material, wq); 152 } 153 154 private static int GetWindQuality(Material material, int windQuality = -1) 155 { 156 // Conservative wind quality priority: 157 // input WindQuality > enabled keyword > _WindQuality float value 158 if (!WindIntValid(windQuality)) 159 { 160 windQuality = material.HasProperty(Uniforms._WindQuality) ? (int)material.GetFloat(Uniforms._WindQuality) : 0; 161 if (!WindIntValid(windQuality)) 162 { 163 windQuality = GetWindQualityFromKeywords(material.shaderKeywords); 164 if (!WindIntValid(windQuality)) 165 windQuality = 0; 166 } 167 } 168 return windQuality; 169 } 170 171 private static void ClearWindKeywords(Material material) 172 { 173 if (material == null) 174 return; 175 for (int i = 0; i < (int)WindQuality.Count; i++) 176 { 177 material.DisableKeyword(WindQualityString[i]); 178 } 179 } 180 181 private static void SetWindQuality(Material material, int windQuality) 182 { 183 Debug.Assert(WindIntValid(windQuality), "Attempting to set invalid wind quality on material " + material.name); 184 185 if (material == null) 186 return; 187 188 if (windQuality != GetWindQualityFromKeywords(material.shaderKeywords)) 189 { 190 ClearWindKeywords(material); 191 } 192 193 material.EnableKeyword(WindQualityString[windQuality]); 194 material.SetFloat(Uniforms._WindQuality, windQuality); // A legacy float used in native code to apply wind data 195 if (material.HasProperty("_WINDQUALITY")) 196 material.SetFloat(Uniforms._WINDQUALITY, windQuality); // The actual name of the keyword enum for the shadergraph 197 } 198 199 private static int GetWindQualityFromKeywords(string[] matKws) 200 { 201 foreach (string kw in matKws) 202 { 203 if (kw.StartsWith("_WINDQUALITY_")) 204 { 205 for (int i = 0; i < (int)WindQuality.Count; i++) 206 { 207 if (kw.EndsWith(WindQualityString[i])) 208 return i; 209 } 210 } 211 } 212 return -1; 213 } 214 215 private static bool WindIntValid(int windInt) 216 { 217 return ((int)WindQuality.None <= windInt) && (windInt < (int)WindQuality.Count); 218 } 219 } 220}