A game about forced loneliness, made by TACStudios
1using System; 2using System.Runtime.CompilerServices; 3using UnityEngine.Experimental.Rendering; 4 5namespace UnityEngine.Rendering 6{ 7 // Due to limitations in the builtin Gradient we need this custom wrapper. 8 9 /// <summary> 10 /// A wrapper around <c>Gradient</c> to automatically bake it into a texture. 11 /// </summary> 12 [Serializable] 13 public class TextureGradient : IDisposable 14 { 15 /// <summary> 16 /// Texture Size computed. 17 /// </summary> 18 [field: SerializeField, HideInInspector] 19 public int textureSize { get; private set; } 20 21 /// <summary> 22 /// Internal Gradient used to generate the Texture 23 /// </summary> 24 [SerializeField] 25 Gradient m_Gradient; 26 27 Texture2D m_Texture = null; 28 29 int m_RequestedTextureSize = -1; 30 31 bool m_IsTextureDirty; 32 bool m_Precise; 33 34 /// <summary>All color keys defined in the gradient.</summary> 35 [HideInInspector] 36 public GradientColorKey[] colorKeys => m_Gradient?.colorKeys; 37 38 /// <summary>All alpha keys defined in the gradient.</summary> 39 [HideInInspector] 40 public GradientAlphaKey[] alphaKeys => m_Gradient?.alphaKeys; 41 42 /// <summary>Controls how the gradient colors are interpolated.</summary> 43 [SerializeField, HideInInspector] 44 public GradientMode mode = GradientMode.PerceptualBlend; 45 46 /// <summary>Indicates the color space that the gradient color keys are using.</summary> 47 [SerializeField, HideInInspector] 48 public ColorSpace colorSpace = ColorSpace.Uninitialized; 49 50 /// <summary> 51 /// Creates a new <see cref="TextureGradient"/> from an existing <c>Gradient</c>. 52 /// </summary> 53 /// <param name="baseCurve">The source <c>Gradient</c>.</param> 54 public TextureGradient(Gradient baseCurve) 55 : this(baseCurve.colorKeys, baseCurve.alphaKeys) 56 { 57 mode = baseCurve.mode; 58 colorSpace = baseCurve.colorSpace; 59 m_Gradient.mode = baseCurve.mode; 60 m_Gradient.colorSpace = baseCurve.colorSpace; 61 } 62 63 /// <summary> 64 /// Creates a new <see cref="TextureCurve"/> from an arbitrary number of keyframes. 65 /// </summary> 66 /// <param name="colorKeys">An array of keyframes used to define the color of gradient.</param> 67 /// <param name="alphaKeys">An array of keyframes used to define the alpha of gradient.</param> 68 /// <param name="mode">Indicates the color space that the gradient color keys are using.</param> 69 /// <param name="colorSpace">Controls how the gradient colors are interpolated.</param> 70 /// <param name="requestedTextureSize">Texture Size used internally, if '-1' using Nyquist-Shannon limits.</param> 71 /// <param name="precise">if precise uses 4*Nyquist-Shannon limits, 2* otherwise.</param> 72 public TextureGradient(GradientColorKey[] colorKeys, GradientAlphaKey[] alphaKeys, GradientMode mode = GradientMode.PerceptualBlend, ColorSpace colorSpace = ColorSpace.Uninitialized, int requestedTextureSize = -1, bool precise = false) 73 { 74 Rebuild(colorKeys, alphaKeys, mode, colorSpace, requestedTextureSize, precise); 75 } 76 77 void Rebuild(GradientColorKey[] colorKeys, GradientAlphaKey[] alphaKeys, GradientMode mode, ColorSpace colorSpace, int requestedTextureSize, bool precise) 78 { 79 m_Gradient = new Gradient(); 80 m_Gradient.mode = mode; 81 m_Gradient.colorSpace = colorSpace; 82 m_Gradient.SetKeys(colorKeys, alphaKeys); 83 m_Precise = precise; 84 m_RequestedTextureSize = requestedTextureSize; 85 if (requestedTextureSize > 0) 86 { 87 textureSize = requestedTextureSize; 88 } 89 else 90 { 91 float smallestDelta = 1.0f; 92 float[] times = new float[colorKeys.Length + alphaKeys.Length]; 93 for (int i = 0; i < colorKeys.Length; ++i) 94 { 95 times[i] = colorKeys[i].time; 96 } 97 for (int i = 0; i < alphaKeys.Length; ++i) 98 { 99 times[colorKeys.Length + i] = alphaKeys[i].time; 100 } 101 Array.Sort(times); 102 // Found the smallest increment between 2 keys 103 for (int i = 1; i < times.Length; ++i) 104 { 105 int k0 = Math.Max(i - 1, 0); 106 int k1 = Math.Min(i, times.Length - 1); 107 float delta = Mathf.Abs(times[k0] - times[k1]); 108 // Do not compare if time is duplicated 109 if (delta > 0 && delta < smallestDelta) 110 smallestDelta = delta; 111 } 112 113 // Nyquist-Shannon 114 // smallestDelta: 1.00f => Sampling => 2 115 // smallestDelta: 0.50f => Sampling => 3 116 // smallestDelta: 0.33f => Sampling => 4 117 // smallestDelta: 0.25f => Sampling => 5 118 119 // 2x: Theoretical limits 120 // 4x: Preserve original frequency 121 122 // Round to the closest 4 * Nyquist-Shannon limits 123 // 4x for Fixed to capture sharp discontinuity 124 float scale; 125 if (precise || mode == GradientMode.Fixed) 126 scale = 4.0f; 127 else 128 scale = 2.0f; 129 float sizef = scale * Mathf.Ceil(1.0f / smallestDelta + 1.0f); 130 textureSize = Mathf.RoundToInt(sizef); 131 // Arbitrary max (1024) 132 textureSize = Math.Min(textureSize, 1024); 133 } 134 135 SetDirty(); 136 } 137 138 /// <summary> 139 /// Cleans up the internal texture resource. 140 /// </summary> 141 public void Dispose() 142 { 143 //Release(); 144 } 145 146 /// <summary> 147 /// Releases the internal texture resource. 148 /// </summary> 149 public void Release() 150 { 151 if (m_Texture != null) 152 CoreUtils.Destroy(m_Texture); 153 m_Texture = null; 154 } 155 156 /// <summary> 157 /// Marks the curve as dirty to trigger a redraw of the texture the next time <see cref="GetTexture"/> 158 /// is called. 159 /// </summary> 160 [MethodImpl(MethodImplOptions.AggressiveInlining)] 161 public void SetDirty() 162 { 163 m_IsTextureDirty = true; 164 } 165 166 static GraphicsFormat GetTextureFormat() 167 { 168 return GraphicsFormat.R8G8B8A8_UNorm; 169 } 170 171 /// <summary> 172 /// Gets the texture representation of this Gradient. 173 /// </summary> 174 /// <returns>A texture.</returns> 175 public Texture2D GetTexture() 176 { 177 float step = 1.0f / (float)(textureSize - 1); 178 179 if (m_Texture != null && m_Texture.width != textureSize) 180 { 181 Object.DestroyImmediate(m_Texture); 182 m_Texture = null; 183 } 184 185 if (m_Texture == null) 186 { 187 m_Texture = new Texture2D(textureSize, 1, GetTextureFormat(), TextureCreationFlags.None); 188 m_Texture.name = "GradientTexture"; 189 m_Texture.hideFlags = HideFlags.HideAndDontSave; 190 m_Texture.filterMode = FilterMode.Bilinear; 191 m_Texture.wrapMode = TextureWrapMode.Clamp; 192 m_Texture.anisoLevel = 0; 193 m_IsTextureDirty = true; 194 } 195 196 if (m_IsTextureDirty) 197 { 198 var pixels = new Color[textureSize]; 199 200 for (int i = 0; i < textureSize; i++) 201 pixels[i] = Evaluate(i * step); 202 203 m_Texture.SetPixels(pixels); 204 m_Texture.Apply(false, false); 205 m_IsTextureDirty = false; 206 m_Texture.IncrementUpdateCount(); 207 } 208 209 return m_Texture; 210 } 211 212 /// <summary> 213 /// Evaluate a time value on the Gradient. 214 /// </summary> 215 /// <param name="time">The time within the Gradient you want to evaluate.</param> 216 /// <returns>The value of the Gradient, at the point in time specified.</returns> 217 public Color Evaluate(float time) 218 { 219 if (textureSize <= 0) 220 return Color.black; 221 222 return m_Gradient.Evaluate(time); 223 } 224 225 226 /// <summary> 227 /// Setup Gradient with an array of color keys and alpha keys. 228 /// </summary> 229 /// <param name="colorKeys">Color keys of the gradient (maximum 8 color keys).</param> 230 /// <param name="alphaKeys">Alpha keys of the gradient (maximum 8 alpha keys).</param> 231 /// <param name="mode">Indicates the color space that the gradient color keys are using.</param> 232 /// <param name="colorSpace">Controls how the gradient colors are interpolated.</param> 233 public void SetKeys(GradientColorKey[] colorKeys, GradientAlphaKey[] alphaKeys, GradientMode mode, ColorSpace colorSpace) 234 { 235 m_Gradient.SetKeys(colorKeys, alphaKeys); 236 m_Gradient.mode = mode; 237 m_Gradient.colorSpace = colorSpace; 238 // Rebuild will make the TextureGradient Dirty. 239 Rebuild(colorKeys, alphaKeys, mode, colorSpace, m_RequestedTextureSize, m_Precise); 240 } 241 } 242 243 /// <summary> 244 /// A <see cref="VolumeParameter"/> that holds a <see cref="TextureGradient"/> value. 245 /// </summary> 246 [Serializable] 247 public class TextureGradientParameter : VolumeParameter<TextureGradient> 248 { 249 /// <summary> 250 /// Creates a new <see cref="TextureGradientParameter"/> instance. 251 /// </summary> 252 /// <param name="value">The initial value to store in the parameter.</param> 253 /// <param name="overrideState">The initial override state for the parameter.</param> 254 public TextureGradientParameter(TextureGradient value, bool overrideState = false) 255 : base(value, overrideState) { } 256 257 /// <summary> 258 /// Release implementation. 259 /// </summary> 260 public override void Release() => m_Value.Release(); 261 262 // TODO: TextureGradient interpolation 263 } 264}