using System; using UnityEngine; using UnityEngine.Experimental.Rendering; using System.Linq; using System.Reflection; using System.IO; using Object = UnityEngine.Object; #if UNITY_EDITOR using UnityEditor; using UnityEditor.Callbacks; #endif namespace X.Rendering.Assets { public interface IGradientTextureForEditor { void CreateTexture(); Texture2D GetTexture(); void LoadExisitingTexture(); } /// /// Main Asset, holds settings, create, hold and change Texture2D's pixels, name /// [CreateAssetMenu(fileName = "NewGradientName", menuName = "Texture/Gradient")] public class GradientTexture : ScriptableObject, IEquatable, ISerializationCallbackReceiver, IGradientTextureForEditor { [SerializeField] Vector2Int _resolution = new Vector2Int(256, 256); [SerializeField] bool _sRGB = true; [SerializeField] AnimationCurve _verticalLerp = AnimationCurve.Linear(0, 0, 1, 1); [SerializeField, GradientUsage(true)] Gradient _horizontalTop = GetDefaultGradient(); [SerializeField, GradientUsage(true)] Gradient _horizontalBottom = GetDefaultGradient(); [SerializeField, HideInInspector] Texture2D _texture = default; public Texture2D GetTexture() => _texture; public bool GetSRGB() => _sRGB; public void SetSRGB(bool value) { _sRGB = value; OnValidate(); } int _width => _resolution.x; int _height => _resolution.y; public static implicit operator Texture2D(GradientTexture asset) => asset.GetTexture(); static Gradient GetDefaultGradient() => new Gradient { alphaKeys = new[] { new GradientAlphaKey(1, 1) }, colorKeys = new[] { new GradientColorKey(Color.black, 0), new GradientColorKey(Color.white, 1) } }; public void FillColors(bool useRGB) { bool isLinear = QualitySettings.activeColorSpace == ColorSpace.Linear; float tVertical = 0; for (int y = 0; y < _height; y++) { tVertical = _verticalLerp.Evaluate((float)y / _height); for (int x = 0; x < _width; x++) { float tHorizontal = (float)x / _width; Color color = Color.Lerp(_horizontalBottom.Evaluate(tHorizontal), _horizontalTop.Evaluate(tHorizontal), tVertical); color = useRGB && isLinear ? color.linear : color; _texture.SetPixel(x, y, color); } } _texture.Apply(); } public bool Equals(Texture2D other) { return _texture.Equals(other); } void OnValidate() => ValidateTextureValues(); void IGradientTextureForEditor.LoadExisitingTexture() { #if UNITY_EDITOR if (!_texture) { string assetPath = AssetDatabase.GetAssetPath(this); _texture = AssetDatabase.LoadAssetAtPath(assetPath); } #endif } void IGradientTextureForEditor.CreateTexture() { #if UNITY_EDITOR //if (EditorApplication.isUpdating) return; string assetPath = AssetDatabase.GetAssetPath(this); if (string.IsNullOrEmpty(assetPath)) return; if (!_texture && this != null && !EditorApplication.isUpdating) { AssetDatabase.ImportAsset(assetPath); _texture = AssetDatabase.LoadAssetAtPath(assetPath); } if (!_texture) { #if UNITY_2018 _texture = new Texture2D(_resolution.x, _resolution.y); #else _texture = new Texture2D(_resolution.x, _resolution.y, DefaultFormat.LDR, TextureCreationFlags.None); #endif if (_texture.name != name) _texture.name = name; } if (!_texture) return; ValidateTextureValues(); if (!EditorUtility.IsPersistent(this)) return; if (AssetDatabase.IsSubAsset(_texture)) return; if (AssetDatabase.LoadAssetAtPath(assetPath)) return; #if UNITY_2020_1_OR_NEWER if (AssetDatabase.IsAssetImportWorkerProcess()) return; #endif AssetDatabase.AddObjectToAsset(_texture, this); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate); #endif } void ValidateTextureValues() { if (!_texture) return; if (_texture.name != name) { _texture.name = name; } else { if (_texture.width != _resolution.x || _texture.height != _resolution.y) { #if UNITY_2022_1_OR_NEWER _texture.Reinitialize(_resolution.x, _resolution.y); #else _texture.Resize(_resolution.x, _resolution.y); #endif } #if UNITY_EDITOR _texture.alphaIsTransparency = true; #endif FillColors(_sRGB); SetDirtyTexture(); } } #region Editor [System.Diagnostics.Conditional("UNITY_EDITOR")] void SetDirtyTexture() { #if UNITY_EDITOR if (!_texture) return; EditorUtility.SetDirty(_texture); #endif } #endregion public void OnAfterDeserialize() { } public void OnBeforeSerialize() { #if UNITY_EDITOR if (!_texture || _texture.name == name) return; _texture.name = name; //AssetDatabase.SaveAssets(); #endif } } #if UNITY_EDITOR [CustomEditor(typeof(GradientTexture), true), CanEditMultipleObjects] public class GradientTextureEditor : UnityEditor.Editor { GradientTexture _gradientTexture; UnityEditor.Editor _editor; public override bool HasPreviewGUI() => true; void OnEnable() { _gradientTexture = target as GradientTexture; } public override void OnInspectorGUI() { if (_gradientTexture.GetTexture() == null) { (_gradientTexture as IGradientTextureForEditor).CreateTexture(); } base.OnInspectorGUI(); string buttonText = "Encode to PNG" + (targets.Length > 1 ? $" ({targets.Length})" : ""); if (GUILayout.Button(buttonText)) { foreach (Object target in targets) { GradientTexture targetTexture = target as GradientTexture; string path = EditorUtility.SaveFilePanelInProject("Save file", $"{targetTexture.name}_baked", "png", "Choose path to save file"); if (string.IsNullOrEmpty(path)) { Debug.LogError("[ GradientTextureEditor ] EncodeToPNG() save path is empty! canceled", targetTexture); return; } bool wasSRGB = targetTexture.GetSRGB(); if (wasSRGB) targetTexture.SetSRGB(false); byte[] bytes = ImageConversion.EncodeToPNG(targetTexture.GetTexture()); targetTexture.SetSRGB(wasSRGB); int length = "Assets".Length; string dataPath = Application.dataPath; dataPath = dataPath.Remove(dataPath.Length - length, length); dataPath += path; File.WriteAllBytes(dataPath, bytes); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); AssetDatabase.ImportAsset(path); Texture2D image = AssetDatabase.LoadAssetAtPath(path); TextureImporter importer = (TextureImporter)AssetImporter.GetAtPath(path); importer.sRGBTexture = targetTexture.GetSRGB(); importer.SaveAndReimport(); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); Debug.Log($"[ GradientTextureEditor ] EncodeToPNG() Success! png-gradient saved at '{path}'", image); EditorGUIUtility.PingObject(image); Selection.activeObject = image; } } } public override void DrawPreview(Rect previewArea) { Texture2D texture = _gradientTexture.GetTexture(); bool check = !_editor || _editor.target != texture; if (check && texture && (_editor == null || _editor.target != texture)) { try { _editor = CreateEditor(targets.Select(t => (t as GradientTexture)?.GetTexture()).ToArray()); } catch { _editor = null; //Debug.LogException(e); //throw; } } if (_editor && _editor.target) { try { _editor.DrawPreview(previewArea); } catch { //Debug.LogException(e); //throw; } } } public override void OnPreviewSettings() { if (_editor && _editor.target) { try { _editor.OnPreviewSettings(); } catch { //Debug.LogException(e); //throw; } } } public override void ReloadPreviewInstances() { if (_editor && _editor.target) { try { _editor.ReloadPreviewInstances(); } catch { //Debug.LogException(e); //throw; } } } public override void OnInteractivePreviewGUI(Rect r, GUIStyle background) { if (_editor && _editor.target) { try { _editor.OnInteractivePreviewGUI(r, background); } catch { //Debug.LogException(e); //throw; } } } public override void OnPreviewGUI(Rect r, GUIStyle background) { if (_editor && _editor.target) { try { _editor.OnPreviewGUI(r, background); } catch { //Debug.LogException(e); //throw; } } } public override Texture2D RenderStaticPreview(string assetPath, Object[] subAssets, int width, int height) { if (_gradientTexture == null) return null; if (_gradientTexture.GetTexture() == null) return null; Texture2D tex = new Texture2D(width, height); EditorUtility.CopySerialized(_gradientTexture.GetTexture(), tex); return tex; } void OnDisable() { if (_editor) { _editor.GetType().GetMethod("OnDisable", BindingFlags.NonPublic)?.Invoke(_editor, null); } } void OnDestroy() { if (_editor) { DestroyImmediate(_editor); } } } public static class DragAndDropUtility { static DragAndDrop.ProjectBrowserDropHandler _handlerProject; [InitializeOnLoadMethod] public static void Init() { _handlerProject = ProjectDropHandler; DragAndDrop.RemoveDropHandler(_handlerProject); DragAndDrop.AddDropHandler(_handlerProject); } private static DragAndDropVisualMode ProjectDropHandler(int dragInstanceId, string dropUponPath, bool perform) { if (!perform) { var dragged = DragAndDrop.objectReferences; bool found = false; for (var i = 0; i < dragged.Length; i++) { if (dragged[i] is GradientTexture gradient) { dragged[i] = gradient.GetTexture(); found = true; } } if (found) { DragAndDrop.objectReferences = dragged; GUI.changed = true; return default; } } return default; } } public class ProjectIconsUtility { [DidReloadScripts] static ProjectIconsUtility() { EditorApplication.projectWindowItemOnGUI -= ItemOnGUI; EditorApplication.projectWindowItemOnGUI += ItemOnGUI; } static void ItemOnGUI(string guid, Rect rect) { string assetPath = AssetDatabase.GUIDToAssetPath(guid); var asset = AssetDatabase.LoadAssetAtPath(assetPath); if (asset == null) return; if (!asset.GetTexture()) return; if (rect.height > 30) { // rect.position = new Vector2(rect.position.x + rect.height * .1f + rect.width - rect.height, rect.position.y); // rect.height *= .87f; // rect.width = rect.height - 5; // rect.height *= .95f; } else { //rect.position = new Vector2(rect.position.x-2 + rect.width - rect.height, rect.position.y); rect.width = rect.height *= .9f; GUI.DrawTexture(rect, asset.GetTexture()); } } } #endif }