A game about forced loneliness, made by TACStudios
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}