using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.Experimental.Rendering; namespace UnityEditor.Rendering { /// /// Formats the provided descriptor into a temperature unit slider with contextual slider markers, tooltips, and icons. /// class TemperatureSlider : LightUnitSlider { private Vector3 m_ExponentialConstraints; private LightEditor.Settings m_Settings; private static Texture2D s_KelvinGradientTexture; /// /// Exponential slider modeled to set a f(0.5) value. /// ref: https://stackoverflow.com/a/17102320 /// void PrepareExponentialConstraints(float lo, float mi, float hi) { // float x = lo; // float y = mi; // float z = hi; // // // https://www.desmos.com/calculator/yx2yf4huia // m_ExponentialConstraints.x = ((x * z) - (y * y)) / (x - (2 * y) + z); // m_ExponentialConstraints.y = ((y - x) * (y - x)) / (x - (2 * y) + z); // m_ExponentialConstraints.z = 2 * Mathf.Log((z - y) / (y - x)); // Warning: These are the coefficients for a system of equation fit for a continuous, monotonic curve that fits a f(0.44) value. // f(0.44) is required instead of f(0.5) due to the location of the white in the temperature gradient texture. // The equation is solved to get the coefficient for the following constraint for low, mid, hi: // f(0) = 1500 // f(0.44) = 6500 // f(1.0) = 20000 // If for any reason the constraints are changed, then the function must be refit and the new coefficients found. // Note that we can't re-use the original PowerSlider instead due to how it forces a text field, which we don't want in this case. m_ExponentialConstraints.x = -3935.53965427f; m_ExponentialConstraints.y = 5435.53965427f; m_ExponentialConstraints.z = 1.48240556f; } protected float ValueToSlider(float x) => Mathf.Log((x - m_ExponentialConstraints.x) / m_ExponentialConstraints.y) / m_ExponentialConstraints.z; protected float SliderToValue(float x) => m_ExponentialConstraints.x + m_ExponentialConstraints.y * Mathf.Exp(m_ExponentialConstraints.z * x); protected override float GetPositionOnSlider(float value, Vector2 valueRange) { return ValueToSlider(value); } static Texture2D GetKelvinGradientTexture(LightEditor.Settings settings) { if (s_KelvinGradientTexture == null) { var kelvinTexture = (Texture2D)typeof(LightEditor.Settings).GetField("m_KelvinGradientTexture", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(settings); // This seems to be the only way to gamma-correct the internal gradient tex (aside from drawing it manually). var kelvinTextureLinear = new Texture2D(kelvinTexture.width, kelvinTexture.height, GraphicsFormat.R8G8B8A8_SRGB, TextureCreationFlags.MipChain); kelvinTextureLinear.SetPixels(kelvinTexture.GetPixels()); kelvinTextureLinear.Apply(); s_KelvinGradientTexture = kelvinTextureLinear; } return s_KelvinGradientTexture; } public TemperatureSlider(LightUnitSliderUIDescriptor descriptor) : base(descriptor) { var halfValue = 6500; PrepareExponentialConstraints(m_Descriptor.sliderRange.x, halfValue, m_Descriptor.sliderRange.y); } public void Setup(LightEditor.Settings settings) { m_Settings = settings; } // The serialized property for color temperature is stored in the build-in light editor, and we need to use this object to apply the update. protected override void SetValueToPreset(SerializedProperty value, LightUnitSliderUIRange preset) { m_Settings.Update(); base.SetValueToPreset(value, preset); m_Settings.ApplyModifiedProperties(); } protected override void DoSlider(Rect rect, ref float value, Vector2 sliderRange) { SliderWithTextureNoTextField(rect, ref value, sliderRange, m_Settings); } // Note: We could use the internal SliderWithTexture, however: the internal slider func forces a text-field (and no ability to opt-out of it). void SliderWithTextureNoTextField(Rect rect, ref float value, Vector2 range, LightEditor.Settings settings) { GUI.DrawTexture(rect, GetKelvinGradientTexture(settings)); EditorGUI.BeginChangeCheck(); // Draw the exponential slider that fits 6500K to the white point on the gradient texture. var internalValue = GUI.HorizontalSlider(rect, ValueToSlider(value), 0f, 1f, SliderStyles.k_TemperatureBorder, SliderStyles.k_TemperatureThumb); // Round to nearest since so much precision is not necessary for kelvin while sliding. if (EditorGUI.EndChangeCheck()) { // Map the value back into kelvin. value = SliderToValue(internalValue); value = Mathf.Round(value); } } } /// /// Helper to draw a temperature slider on the inspector /// public class TemperatureSliderUIDrawer { static TemperatureSlider k_TemperatureSlider; static TemperatureSliderUIDrawer() { // Kelvin is not classified internally as a light unit so we handle it independently as well. k_TemperatureSlider = new TemperatureSlider(LightUnitSliderDescriptors.TemperatureDescriptor); } /// /// Draws a temperature slider /// /// The light settings /// The serialized object /// The serialized property /// The rect where the slider will be drawn public static void Draw(LightEditor.Settings settings, SerializedObject serializedObject, SerializedProperty value, Rect rect) { k_TemperatureSlider.SetSerializedObject(serializedObject); using (new EditorGUI.IndentLevelScope(-EditorGUI.indentLevel)) { k_TemperatureSlider.Setup(settings); float val = value.floatValue; k_TemperatureSlider.Draw(rect, value, ref val); if (val != value.floatValue) value.floatValue = val; } } } }