res-avatar-unity/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/TexturePacker.cs

1485 lines
69 KiB
C#
Raw Permalink Normal View History

2023-07-16 02:51:23 +00:00
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace Thry
{
public class TexturePacker : EditorWindow
{
const int MIN_WIDTH = 850;
const int MIN_HEIGHT = 790;
[MenuItem("Thry/Texture Packer", priority = 100)]
public static TexturePacker ShowWindow()
{
TexturePacker packer = (TexturePacker)GetWindow(typeof(TexturePacker));
packer.minSize = new Vector2(MIN_WIDTH, MIN_HEIGHT);
packer.titleContent = new GUIContent("Thry Texture Packer");
packer.OnSave = null; // clear save callback
packer.OnChange = null; // clear save callback
return packer;
}
[MenuItem("Assets/Thry/Open in Texture Packer")]
public static void OpenInTexturePacker()
{
TexturePacker packer = ShowWindow();
packer.InitilizeWithOneTexture(Selection.activeObject as Texture2D);
}
[MenuItem("Assets/Thry/Open in Texture Packer", true)]
public static bool OpenInTexturePackerValidate()
{
return Selection.activeObject is Texture2D;
}
#region DataStructures
public enum TextureChannelIn { R, G, B, A, Max, None }
public enum TextureChannelOut { R, G, B, A, None }
public enum BlendMode { Add, Multiply, Max, Min }
public enum InvertMode { None, Invert}
public enum SaveType { PNG, JPG }
public enum InputType { Texture, Color, Gradient }
public enum GradientDirection { Horizontal, Vertical }
public enum KernelPreset { None, Custom, EdgeDetection, Sharpen, GaussianBlur3x3, GaussianBlur5x5 }
[Serializable]
public class ImageAdjust
{
public float Brightness = 1;
public float Hue = 0;
public float Saturation = 1;
public float Rotation = 0;
public Vector2 Scale = Vector2.one;
public Vector2 Offset = Vector2Int.zero;
public bool ChangeCheck = false;
public Vector2Int Resolution = new Vector2Int(16, 16);
}
static string GetTypeEnding(SaveType t)
{
switch (t)
{
case SaveType.PNG: return ".png";
case SaveType.JPG: return ".jpg";
default: return ".png";
}
}
[Serializable]
public class OutputConfig
{
public BlendMode BlendMode;
public InvertMode Invert;
public float Fallback;
public OutputConfig(float fallback = 0)
{
Fallback = fallback;
}
}
[Serializable]
public class TextureSource
{
public Texture2D Texture;
public long LastHandledTextureEditTime;
public FilterMode FilterMode;
public Color Color;
public Gradient Gradient;
public GradientDirection GradientDirection;
public Texture2D GradientTexture;
public Texture2D TextureTexture;
InputType _inputType = InputType.Texture;
public InputType InputType
{
get
{
return _inputType;
}
set
{
if(_inputType != value)
{
_inputType = value;
if(_inputType == InputType.Texture) Texture = TextureTexture;
if(_inputType == InputType.Gradient) Texture = GradientTexture;
}
}
}
public TextureSource()
{
}
public TextureSource(Texture2D tex)
{
SetTexture(tex);
}
public void SetTexture(Texture2D tex)
{
Texture = tex;
FilterMode = tex != null ? tex.filterMode : FilterMode.Bilinear;
}
public static void SetUncompressedTextureDirty(Texture2D tex)
{
if (_cachedUncompressedTextures.ContainsKey(tex))
{
_cachedUncompressedTexturesNeedsReload[tex] = true;
}
}
static Dictionary<Texture2D, Texture2D> _cachedUncompressedTextures = new Dictionary<Texture2D, Texture2D>();
static Dictionary<Texture2D, bool> _cachedUncompressedTexturesNeedsReload = new Dictionary<Texture2D, bool>();
public Texture2D UncompressedTexture
{
get
{
if(_cachedUncompressedTextures.ContainsKey(Texture) == false || _cachedUncompressedTexturesNeedsReload[Texture])
{
string path = AssetDatabase.GetAssetPath(Texture);
if(path.EndsWith(".png") || path.EndsWith(".jpg"))
{
EditorUtility.DisplayProgressBar("Loading Raw PNG", "Loading " + path, 0.5f);
Texture2D tex = new Texture2D(2,2, TextureFormat.RGBA32, false, true);
tex.LoadImage(System.IO.File.ReadAllBytes(path));
tex.filterMode = Texture.filterMode;
_cachedUncompressedTextures[Texture] = tex;
EditorUtility.ClearProgressBar();
}else if (path.EndsWith(".tga"))
{
Texture2D tex = TextureHelper.LoadTGA(path, true);
tex.filterMode = Texture.filterMode;
_cachedUncompressedTextures[Texture] = tex;
}
else
{
_cachedUncompressedTextures[Texture] = Texture;
}
_cachedUncompressedTexturesNeedsReload[Texture] = false;
}
return _cachedUncompressedTextures[Texture];
}
}
public void FindMaxSize(ref int width, ref int height)
{
if (Texture == null) return;
width = Mathf.Max(width, UncompressedTexture.width);
height = Mathf.Max(height, UncompressedTexture.height);
}
}
[Serializable]
public class Connection
{
public int FromTextureIndex = -1;
public TextureChannelIn FromChannel = TextureChannelIn.None;
public TextureChannelOut ToChannel = TextureChannelOut.None;
public static Connection CreateFull(int index, TextureChannelIn channel, TextureChannelOut toChannel)
{
Connection connection = new Connection();
connection.FromTextureIndex = index;
connection.FromChannel = channel;
connection.ToChannel = toChannel;
return connection;
}
public static Connection Create(int index, TextureChannelIn channel)
{
Connection connection = new Connection();
connection.FromTextureIndex = index;
connection.FromChannel = channel;
return connection;
}
public static Connection Create(TextureChannelOut channel)
{
Connection connection = new Connection();
connection.ToChannel = channel;
return connection;
}
public void SetFrom(int index, TextureChannelIn channel, TexturePacker packer)
{
// cancle if selecting same channel
if(FromTextureIndex == index && FromChannel == channel)
{
packer._creatingConnection = null;
return;
}
// set
FromTextureIndex = index;
FromChannel = channel;
// check if done
if(ToChannel == TextureChannelOut.None) return;
// check if already exists
if(packer._connections.Exists(c => c.ToChannel == ToChannel && c.FromTextureIndex == FromTextureIndex && c.FromChannel == FromChannel))
{
packer._creatingConnection = null;
return;
}
packer._connections.Add(this);
packer._creatingConnection = null;
packer._changeCheckForPacking = true;
}
public void SetTo(TextureChannelOut channel, TexturePacker packer)
{
// cancle if selecting same channel
if (ToChannel == channel)
{
return;
}
// set
ToChannel = channel;
// check if done
if(FromTextureIndex == -1 || FromChannel == TextureChannelIn.None) return;
// check if already exists
if(packer._connections.Exists(c => c.ToChannel == ToChannel && c.FromTextureIndex == FromTextureIndex && c.FromChannel == FromChannel))
{
packer._creatingConnection = null;
return;
}
packer._connections.Add(this);
packer._creatingConnection = null;
packer._changeCheckForPacking = true;
}
Vector3 _bezierStart, _bezierEnd, _bezierStartTangent, _bezierEndTangent;
public void CalculateBezierPoints(Vector2[] positionsIn, Vector2[] positionsOut)
{
_bezierStart = positionsIn[FromTextureIndex * 5 + (int)FromChannel];
_bezierEnd = positionsOut[(int)ToChannel];
_bezierStartTangent = _bezierStart + Vector3.right * 50;
_bezierEndTangent = _bezierEnd + Vector3.left * 50;
}
public Vector3 BezierStart { get { return _bezierStart; } }
public Vector3 BezierEnd { get { return _bezierEnd; } }
public Vector3 BezierStartTangent { get { return _bezierStartTangent; } }
public Vector3 BezierEndTangent { get { return _bezierEndTangent; } }
}
struct InteractionWithConnection
{
public Connection connection;
public float distanceX;
}
#endregion
const string CHANNEL_PREVIEW_SHADER = "Hidden/Thry/ChannelPreview";
TextureSource[] _textureSources = new TextureSource[]
{
new TextureSource(),
new TextureSource(),
new TextureSource(),
new TextureSource(),
};
OutputConfig[] _outputConfigs = new OutputConfig[]
{
new OutputConfig(0),
new OutputConfig(0),
new OutputConfig(0),
new OutputConfig(1),
};
Vector2 _scrollPosition;
TexturePackerConfig _config;
List<Connection> _connections = new List<Connection>();
Connection _creatingConnection;
Texture2D _outputTexture;
ColorSpace _colorSpace = ColorSpace.Uninitialized;
FilterMode _filterMode = FilterMode.Bilinear;
string _saveFolder;
string _saveName;
SaveType _saveType = SaveType.PNG;
float _saveQuality = 1;
KernelPreset _kernelPreset = KernelPreset.None;
bool _kernelEditHorizontal = true;
float[] _kernel_x = GetKernelPreset(KernelPreset.None, true);
float[] _kernel_y = GetKernelPreset(KernelPreset.None, false);
int _kernel_loops = 1;
float _kernel_strength = 1;
bool _kernel_twoPass;
bool _kernel_grayScale;
bool[] _kernel_channels = new bool[4] { true, true, true, true };
bool[] _channel_export = new bool[4] { true, true, true, true };
ImageAdjust _imageAdjust = new ImageAdjust();
public Action<Texture2D> OnSave;
public Action<Texture2D, TextureSource[], OutputConfig[], Connection[]> OnChange;
static Material s_channelPreviewMaterial;
static Material ChannelPreviewMaterial
{
get
{
if (s_channelPreviewMaterial == null)
{
s_channelPreviewMaterial = new Material(Shader.Find(CHANNEL_PREVIEW_SHADER));
}
return s_channelPreviewMaterial;
}
}
static ComputeShader s_computeShader;
static ComputeShader ComputeShader
{
get
{
if (s_computeShader == null)
{
s_computeShader = AssetDatabase.LoadAssetAtPath<ComputeShader>(AssetDatabase.GUIDToAssetPath("56f54c5664777a747b2552701571174d"));
}
return s_computeShader;
}
}
Vector2[] _positionsChannelIn = new Vector2[20];
Vector2[] _positionsChannelOut = new Vector2[4];
Rect[] _rectsChannelIn = new Rect[20];
Rect[] _rectsChannelOut = new Rect[4];
public void InitilizeWithData(TextureSource[] sources, OutputConfig[] configs, IEnumerable<Connection> connections, FilterMode filterMode, ColorSpace colorSpace)
{
_textureSources = new TextureSource[]
{
new TextureSource(),
new TextureSource(),
new TextureSource(),
new TextureSource(),
};
Array.Copy(sources, _textureSources, sources.Length);
_outputConfigs = configs;
_connections = connections.ToList();
_filterMode = filterMode;
_colorSpace = colorSpace;
// Reset Color Adjust
_imageAdjust = new ImageAdjust();
DeterminePathAndFileNameIfEmpty(true);
DetermineImportSettings();
Pack();
}
void InitilizeWithOneTexture(Texture2D texture)
{
_connections.Clear();
_textureSources[0].SetTexture(texture);
_textureSources[1].SetTexture(texture);
_textureSources[2].SetTexture(texture);
_textureSources[3].SetTexture(texture);
// Add connections
_connections.Add(Connection.CreateFull(0, TextureChannelIn.R, TextureChannelOut.R));
_connections.Add(Connection.CreateFull(1, TextureChannelIn.G, TextureChannelOut.G));
_connections.Add(Connection.CreateFull(2, TextureChannelIn.B, TextureChannelOut.B));
_connections.Add(Connection.CreateFull(3, TextureChannelIn.A, TextureChannelOut.A));
// Reset Color Adjust
_imageAdjust = new ImageAdjust();
DeterminePathAndFileNameIfEmpty(true);
DetermineImportSettings();
DetermineOutputResolution(_textureSources, _imageAdjust);
Pack();
}
const int TOP_OFFSET = 50;
const int INPUT_PADDING = 20;
const int OUTPUT_HEIGHT = 300;
bool _changeCheckForPacking;
private void OnGUI()
{
_scrollPosition = GUILayout.BeginScrollView(_scrollPosition);
DrawConfigGUI();
// Draw three texture slots on the left, a space in the middle, and one texutre slot on the right
GUILayout.BeginVertical();
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
_changeCheckForPacking = false;
GUILayout.BeginVertical();
GUILayout.Space(TOP_OFFSET);
bool didInputTexturesChange = false;
didInputTexturesChange |= DrawInput( _textureSources[0], 0);
GUILayout.Space(INPUT_PADDING);
didInputTexturesChange |= DrawInput( _textureSources[1], 1);
GUILayout.Space(INPUT_PADDING);
didInputTexturesChange |= DrawInput( _textureSources[2], 2);
GUILayout.Space(INPUT_PADDING);
didInputTexturesChange |= DrawInput( _textureSources[3], 3);
GUILayout.EndVertical();
float inputHeight = 120 * 4 + INPUT_PADDING * 3 + TOP_OFFSET;
GUILayout.Space(400);
Rect rect_outputAndSettings = EditorGUILayout.BeginVertical();
float output_y_offset = TOP_OFFSET + (inputHeight - TOP_OFFSET - OUTPUT_HEIGHT) / 2;
GUILayout.Space(output_y_offset);
DrawOutput(_outputTexture, OUTPUT_HEIGHT);
EditorGUILayout.Space(15);
Rect backgroundImageSettings = EditorGUILayout.BeginVertical();
backgroundImageSettings = new RectOffset(5, 5, 5, 5).Add(backgroundImageSettings);
GUI.DrawTexture(backgroundImageSettings, Texture2D.whiteTexture, ScaleMode.StretchToFill, true, 1, Styles.COLOR_BACKGROUND_1, 0, 10);
EditorGUI.BeginChangeCheck();
_colorSpace = (ColorSpace)EditorGUILayout.EnumPopup(_colorSpace);
_filterMode = (FilterMode)EditorGUILayout.EnumPopup(_filterMode);
_changeCheckForPacking |= EditorGUI.EndChangeCheck();
// Make the sliders delayed, else the UX feels terrible
EditorGUI.BeginChangeCheck();
EventType eventTypeBeforerSliders = Event.current.type;
bool wasWide = EditorGUIUtility.wideMode;
EditorGUIUtility.wideMode = true;
_imageAdjust.Resolution = EditorGUILayout.Vector2IntField("Resolution", _imageAdjust.Resolution);
_imageAdjust.Scale = EditorGUILayout.Vector2Field("Scale", _imageAdjust.Scale);
_imageAdjust.Offset = EditorGUILayout.Vector2Field("Offset", _imageAdjust.Offset);
_imageAdjust.Rotation = EditorGUILayout.Slider("Rotation", _imageAdjust.Rotation, -180, 180);
_imageAdjust.Hue = EditorGUILayout.Slider("Hue", _imageAdjust.Hue, 0, 1);
_imageAdjust.Saturation = EditorGUILayout.Slider("Saturation", _imageAdjust.Saturation, 0, 3);
_imageAdjust.Brightness = EditorGUILayout.Slider("Brightness", _imageAdjust.Brightness, 0, 3);
_imageAdjust.ChangeCheck |= EditorGUI.EndChangeCheck();
EditorGUIUtility.wideMode = wasWide;
if(_imageAdjust.ChangeCheck && (eventTypeBeforerSliders == EventType.MouseUp || (eventTypeBeforerSliders == EventType.KeyUp && Event.current.keyCode == KeyCode.Return)))
{
_changeCheckForPacking = true;
_imageAdjust.ChangeCheck = false;
}
DrawKernelGUI();
EditorGUILayout.EndVertical();
EditorGUILayout.EndVertical();
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
DrawConnections();
if(didInputTexturesChange)
{
DetermineImportSettings();
}
if(_changeCheckForPacking)
{
Pack();
Repaint();
}
GUILayout.Space(20);
DrawSaveGUI();
GUILayout.EndVertical();
HandleConnectionEditing();
HandleConnectionCreation();
GUILayout.EndScrollView();
}
void HandleConnectionEditing()
{
if(Event.current.type == EventType.MouseDown && Event.current.button == 0)
{
InteractionWithConnection toEdit = CheckIfConnectionClicked();
if(toEdit.connection != null)
{
_connections.Remove(toEdit.connection);
// Remove the connection on one side
if(toEdit.distanceX > 0.5)
toEdit.connection.ToChannel = TextureChannelOut.None;
else
toEdit.connection.FromChannel = TextureChannelIn.None;
_creatingConnection = toEdit.connection;
Pack();
Repaint();
}
}
}
void HandleConnectionCreation()
{
// Connections are not nullable anymore, since they are serialized
if(_creatingConnection != null && (_creatingConnection.FromChannel != TextureChannelIn.None || _creatingConnection.ToChannel != TextureChannelOut.None))
{
// if user clicked anywhere on the screen, stop creating the connection
if(Event.current.type == EventType.MouseUp)
{
// Check if mouse position is over any input / output slot
Vector2 mousePosition = Event.current.mousePosition;
for(int t = 0; t < 4; t++)
{
for(int c = 0; c < 5; c++)
{
if(_rectsChannelIn[t * 5 + c].Contains(mousePosition))
{
_creatingConnection.SetFrom(t, (TextureChannelIn)c, this);
Pack();
Repaint();
return;
}
}
}
for(int c = 0; c < 4; c++)
{
if(_rectsChannelOut[c].Contains(mousePosition))
{
_creatingConnection.SetTo((TextureChannelOut)c, this);
Pack();
Repaint();
return;
}
}
_creatingConnection = null;
return;
}
Vector2 bezierStart, bezierEnd, bezierStartTangent, bezierEndTangent;
Color color = Color.white;
bezierEnd = Event.current.mousePosition;
if(_creatingConnection.FromChannel != TextureChannelIn.None)
{
bezierStart = _positionsChannelIn[_creatingConnection.FromTextureIndex * 5 + (int)_creatingConnection.FromChannel];
bezierStartTangent = bezierStart + Vector2.right * 50;
bezierEndTangent = bezierEnd + Vector2.left * 50;
color = GetColor(_creatingConnection.FromChannel);
}
else
{
bezierStart = _positionsChannelOut[(int)_creatingConnection.ToChannel];
bezierStartTangent = bezierStart + Vector2.left * 50;
bezierEndTangent = bezierEnd + Vector2.right * 50;
color = GetColor(_creatingConnection.ToChannel);
}
Handles.DrawBezier(bezierStart, bezierEnd, bezierStartTangent, bezierEndTangent, color, null, 2);
Repaint();
}
}
InteractionWithConnection CheckIfConnectionClicked()
{
Vector2 mousePos = Event.current.mousePosition;
float minDistance = 50;
InteractionWithConnection clickedConnection = new InteractionWithConnection();
foreach(Connection c in _connections)
{
Vector3 from = c.BezierStart;
Vector3 to = c.BezierEnd;
float topY = Mathf.Max(from.y, to.y);
float bottomY = Mathf.Min(from.y, to.y);
float leftX = Mathf.Min(from.x, to.x);
float rightX = Mathf.Max(from.x, to.x);
// check if mouse is in the area of the bezier curve
if(mousePos.x > leftX && mousePos.x < rightX)
{
if(mousePos.y > bottomY && mousePos.y < topY)
{
// check if mouse is close to the bezier curve
float distance = HandleUtility.DistancePointBezier(mousePos, c.BezierStart, c.BezierEnd, c.BezierStartTangent, c.BezierEndTangent);
if(distance < 50)
{
if(distance < minDistance)
{
minDistance = distance;
clickedConnection.connection = c;
clickedConnection.distanceX = (mousePos.x - leftX) / (rightX - leftX);
}
}
}
}
}
return clickedConnection;
}
void DrawConfigGUI()
{
Rect bg = new Rect(position.width / 2 - 150, 10, 300, 30);
Rect rObjField = new RectOffset(5, 5, 5, 5).Remove(bg);
GUI.DrawTexture(bg, Texture2D.whiteTexture, ScaleMode.StretchToFill, true, 0, Styles.COLOR_BACKGROUND_1, 0, 10);
if(_config == null)
{
rObjField = new RectOffset(0, 100, 0, 0).Remove(rObjField);
Rect rButton = new Rect(rObjField.x + rObjField.width + 5, rObjField.y, 95, rObjField.height);
if(GUI.Button(rButton, "New config"))
{
CreateConfig();
}
}
EditorGUI.BeginChangeCheck();
_config = (TexturePackerConfig)EditorGUI.ObjectField(rObjField, _config, typeof(TexturePackerConfig), false);
if(EditorGUI.EndChangeCheck())
{
LoadConfig();
Pack();
Repaint();
}
}
void DrawKernelGUI()
{
Rect r_enum = EditorGUILayout.GetControlRect(false, 20);
EditorGUI.BeginChangeCheck();
_kernelPreset = (KernelPreset)EditorGUI.EnumPopup(r_enum, "Kernel Filter", _kernelPreset);
if(EditorGUI.EndChangeCheck())
{
_kernel_x = _kernelPreset == KernelPreset.Custom ? _kernel_x : GetKernelPreset(_kernelPreset, true);
_kernel_y = _kernelPreset == KernelPreset.Custom ? _kernel_y : GetKernelPreset(_kernelPreset, false);
KernelPresetSetValues(_kernelPreset);
Pack();
Repaint();
}
this.minSize = new Vector2(MIN_WIDTH, _kernelPreset == KernelPreset.None ? MIN_HEIGHT : MIN_HEIGHT + 250);
if(_kernelPreset != KernelPreset.None)
{
EventType eventTypeBeforerSliders = Event.current.type;
_kernel_loops = EditorGUILayout.IntSlider("Loops", _kernel_loops, 1, 25);
_kernel_strength = EditorGUILayout.Slider("Strength", _kernel_strength, 0, 1);
_kernel_twoPass = EditorGUILayout.Toggle("Two Pass", _kernel_twoPass);
_kernel_grayScale = EditorGUILayout.Toggle("Gray Scale", _kernel_grayScale);
Rect r_channels = EditorGUILayout.GetControlRect(false, 20);
r_channels.width /= 4;
float prevLabelWidth = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth = 10;
_kernel_channels[0] = EditorGUI.Toggle(r_channels, "R", _kernel_channels[0]);
r_channels.x += r_channels.width;
_kernel_channels[1] = EditorGUI.Toggle(r_channels, "G", _kernel_channels[1]);
r_channels.x += r_channels.width;
_kernel_channels[2] = EditorGUI.Toggle(r_channels, "B", _kernel_channels[2]);
r_channels.x += r_channels.width;
_kernel_channels[3] = EditorGUI.Toggle(r_channels, "A", _kernel_channels[3]);
EditorGUIUtility.labelWidth = prevLabelWidth;
if(Event.current.type == EventType.Used && (eventTypeBeforerSliders == EventType.MouseUp || (eventTypeBeforerSliders == EventType.KeyUp && Event.current.keyCode == KeyCode.Return)))
{
Pack();
Repaint();
}
if(_kernel_twoPass)
{
Rect r_buttons = EditorGUILayout.GetControlRect(false, 20);
if(GUI.Button( new Rect(r_buttons.x, r_buttons.y, r_buttons.width / 2, r_buttons.height), "X")) _kernelEditHorizontal = true;
if(GUI.Button( new Rect(r_buttons.x + r_buttons.width / 2, r_buttons.y, r_buttons.width / 2, r_buttons.height), "Y")) _kernelEditHorizontal = false;
}
Rect r = EditorGUILayout.GetControlRect(false, 130);
EditorGUI.BeginChangeCheck();
// draw 5x5 matrix inside the r_kernelX rect
for(int x = 0; x < 5; x++)
{
for(int y = 0; y < 5; y++)
{
Rect r_cell = new Rect(r.x + x * r.width / 5, r.y + y * r.height / 5, r.width / 5, r.height / 5);
if(_kernelEditHorizontal || !_kernel_twoPass) _kernel_x[x + y * 5] = EditorGUI.DelayedFloatField(r_cell, _kernel_x[x + y * 5]);
else _kernel_y[x + y * 5] = EditorGUI.DelayedFloatField(r_cell, _kernel_y[x + y * 5]);
}
}
if(EditorGUI.EndChangeCheck())
{
_kernelPreset = KernelPreset.Custom;
Pack();
Repaint();
}
}
}
void DrawSaveGUI()
{
// Saving information
// folder selection
// determine folder & filename from asset name if not set
if(string.IsNullOrEmpty(_saveFolder))
{
DeterminePathAndFileNameIfEmpty();
}
Rect r = EditorGUILayout.BeginHorizontal();
Rect background = new Rect(r.x + r.width / 2 - 400, r.y - 5, 800, 77);
GUI.DrawTexture(background, Texture2D.whiteTexture, ScaleMode.StretchToFill, true, 0, Styles.COLOR_BACKGROUND_1, 0, 10);
GUILayout.FlexibleSpace();
// show current path
GUILayout.Label("Save to: ");
GUILayout.Label(_saveFolder + "\\");
_saveName = GUILayout.TextField(_saveName, GUILayout.MinWidth(50));
_saveType = (SaveType)EditorGUILayout.EnumPopup(_saveType, GUILayout.Width(70));
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if(GUILayout.Button("Change Folder", GUILayout.Width(100)))
{
string path = EditorUtility.OpenFolderPanel("Select folder", _saveFolder, "");
if (!string.IsNullOrEmpty(path))
{
// Make path relative to Assets folder
path = path.Replace(Application.dataPath, "Assets");
_saveFolder = path;
}
}
if(GUILayout.Button("Save", GUILayout.Width(100)))
{
Save();
}
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
GUILayout.Space(10);
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
_channel_export[0] = GUILayout.Toggle(_channel_export[0], "R", GUILayout.Width(26));
_channel_export[1] = GUILayout.Toggle(_channel_export[1], "G", GUILayout.Width(26));
_channel_export[2] = GUILayout.Toggle(_channel_export[2], "B", GUILayout.Width(26));
_channel_export[3] = GUILayout.Toggle(_channel_export[3], "A", GUILayout.Width(26));
if(GUILayout.Button("Export Channels", GUILayout.Width(130)))
{
ExportChannels(false);
}
if(GUILayout.Button("Export Channels (B&W)", GUILayout.Width(150)))
{
ExportChannels(true);
}
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
}
void DeterminePathAndFileNameIfEmpty(bool forceOverwrite = false)
{
foreach(TextureSource s in _textureSources)
{
if(s.Texture != null)
{
string path = AssetDatabase.GetAssetPath(s.Texture);
if(string.IsNullOrWhiteSpace(path))
continue;
if(string.IsNullOrWhiteSpace(_saveFolder) || forceOverwrite)
_saveFolder = Path.GetDirectoryName(path);
if(string.IsNullOrWhiteSpace(_saveName) || forceOverwrite)
_saveName = Path.GetFileNameWithoutExtension(path) + "_packed";
break;
}
}
}
void DetermineImportSettings()
{
_colorSpace = ColorSpace.Gamma;
_filterMode = FilterMode.Bilinear;
foreach(TextureSource s in _textureSources)
{
if(DetermineImportSettings(s))
break;
}
}
bool DetermineImportSettings(TextureSource s)
{
if(s.Texture != null)
{
string path = AssetDatabase.GetAssetPath(s.Texture);
if(path == null)
return false;
TextureImporter importer = AssetImporter.GetAtPath(path) as TextureImporter;
if(importer != null)
{
_colorSpace = importer.sRGBTexture ? ColorSpace.Gamma : ColorSpace.Linear;
_filterMode = importer.filterMode;
return true;
}
}
return false;
}
void DrawConnections()
{
// Draw connections as lines
foreach (Connection c in _connections)
{
c.CalculateBezierPoints(_positionsChannelIn, _positionsChannelOut);
Handles.DrawBezier(c.BezierStart, c.BezierEnd, c.BezierStartTangent, c.BezierEndTangent, GetColor(c.FromChannel), null, 2);
}
}
void DrawOutput(Texture2D texture, int height = 200)
{
int channelWidth = height / 4;
Rect rect = GUILayoutUtility.GetRect(height, height);
// draw 4 channl boxes on the left side
Rect rectR = new Rect(rect.x - channelWidth, rect.y, channelWidth, channelWidth);
Rect rectG = new Rect(rect.x - channelWidth, rect.y + channelWidth, channelWidth, channelWidth);
Rect rectB = new Rect(rect.x - channelWidth, rect.y + channelWidth * 2, channelWidth, channelWidth);
Rect rectA = new Rect(rect.x - channelWidth, rect.y + channelWidth * 3, channelWidth, channelWidth);
// Draw circle button bext to each channel box
int buttonWidth = 80;
int buttonHeight = 40;
Rect buttonR = new Rect(rectR.x - buttonWidth - 5, rectR.y + rectR.height / 2 - buttonHeight / 2, buttonWidth, buttonHeight);
Rect buttonG = new Rect(rectG.x - buttonWidth - 5, rectG.y + rectG.height / 2 - buttonHeight / 2, buttonWidth, buttonHeight);
Rect buttonB = new Rect(rectB.x - buttonWidth - 5, rectB.y + rectB.height / 2 - buttonHeight / 2, buttonWidth, buttonHeight);
Rect buttonA = new Rect(rectA.x - buttonWidth - 5, rectA.y + rectA.height / 2 - buttonHeight / 2, buttonWidth, buttonHeight);
// Draw background
Rect background = new Rect(buttonR.x + 10, rect.y - 5, (rect.x + rect.width + 5) - (buttonR.x + 10), rect.height + 10);
GUI.DrawTexture(background, Texture2D.whiteTexture, ScaleMode.StretchToFill, true, 1, Styles.COLOR_BACKGROUND_1, 0, 10);
EditorGUI.DrawTextureTransparent(rect, texture != null ? texture : Texture2D.blackTexture, ScaleMode.ScaleToFit, 1);
// draw 4 channl boxes on the left side
if (texture != null)
{
ChannelPreviewMaterial.SetTexture("_MainTex", texture);
ChannelPreviewMaterial.SetFloat("_Channel", 0);
EditorGUI.DrawPreviewTexture(rectR, texture, ChannelPreviewMaterial, ScaleMode.ScaleToFit);
ChannelPreviewMaterial.SetFloat("_Channel", 1);
EditorGUI.DrawPreviewTexture(rectG, texture, ChannelPreviewMaterial, ScaleMode.ScaleToFit);
ChannelPreviewMaterial.SetFloat("_Channel", 2);
EditorGUI.DrawPreviewTexture(rectB, texture, ChannelPreviewMaterial, ScaleMode.ScaleToFit);
ChannelPreviewMaterial.SetFloat("_Channel", 3);
EditorGUI.DrawPreviewTexture(rectA, texture, ChannelPreviewMaterial, ScaleMode.ScaleToFit);
}
else
{
EditorGUI.DrawRect(rectR, Color.black);
EditorGUI.DrawRect(rectG, Color.black);
EditorGUI.DrawRect(rectB, Color.black);
EditorGUI.DrawRect(rectA, Color.black);
}
// Draw circle button bext to each channel box
_positionsChannelOut[0] = new Vector2(buttonR.x, buttonR.y + buttonR.height / 2);
_positionsChannelOut[1] = new Vector2(buttonG.x, buttonG.y + buttonG.height / 2);
_positionsChannelOut[2] = new Vector2(buttonB.x, buttonB.y + buttonB.height / 2);
_positionsChannelOut[3] = new Vector2(buttonA.x, buttonA.y + buttonA.height / 2);
_rectsChannelOut[0] = buttonR;
_rectsChannelOut[1] = buttonG;
_rectsChannelOut[2] = buttonB;
_rectsChannelOut[3] = buttonA;
DrawOutputChannel(buttonR, TextureChannelOut.R, _outputConfigs[0]);
DrawOutputChannel(buttonG, TextureChannelOut.G, _outputConfigs[1]);
DrawOutputChannel(buttonB, TextureChannelOut.B, _outputConfigs[2]);
DrawOutputChannel(buttonA, TextureChannelOut.A, _outputConfigs[3]);
}
void DrawOutputChannel(Rect position, TextureChannelOut channel, OutputConfig config)
{
// RGBA on the left side
// fallback or (blendmode & invert) on the right side
Rect channelRect = new Rect(position.x, position.y, 20, position.height);
Rect fallbackRect = new Rect(position.x + 20, position.y, position.width - 20, position.height);
Rect blendmodeRect = new Rect(fallbackRect.x, fallbackRect.y, fallbackRect.width, fallbackRect.height / 2);
Rect invertRect = new Rect(fallbackRect.x, fallbackRect.y + fallbackRect.height / 2, fallbackRect.width, fallbackRect.height / 2);
if(Event.current.type == EventType.MouseDown && channelRect.Contains(Event.current.mousePosition))
{
Event.current.Use();
if (_creatingConnection != null) _creatingConnection.SetTo(channel, this);
else _creatingConnection = Connection.Create(channel);
}
GUI.Button(channelRect, channel.ToString());
EditorGUI.BeginChangeCheck();
if(DoFallback(channel))
{
config.Fallback = EditorGUI.FloatField(fallbackRect, config.Fallback);
}else
{
config.BlendMode = (BlendMode)EditorGUI.EnumPopup(blendmodeRect, config.BlendMode);
config.Invert = (InvertMode)EditorGUI.EnumPopup(invertRect, config.Invert);
}
_changeCheckForPacking |= EditorGUI.EndChangeCheck();
}
bool DrawInput(TextureSource texture, int index, int textureHeight = 100)
{
int channelWidth = textureHeight / 5;
Rect rect = GUILayoutUtility.GetRect(textureHeight, textureHeight + 40);
Rect typeRect = new Rect(rect.x, rect.y, textureHeight, 20);
Rect textureRect = new Rect(rect.x, rect.y + 20, textureHeight, textureHeight);
Rect filterRect = new Rect(textureRect.x, textureRect.y + textureHeight, textureRect.width, 20);
Rect background = new Rect(rect.x - 5, rect.y - 5, rect.width + channelWidth + 40, rect.height + 10);
GUI.DrawTexture(background, Texture2D.whiteTexture, ScaleMode.StretchToFill, true, 1, Styles.COLOR_BACKGROUND_1, 0, 10);
// Draw textrue & filtermode. Change filtermode if texture is changed
EditorGUI.BeginChangeCheck();
texture.InputType = (InputType)EditorGUI.EnumPopup(typeRect, texture.InputType);
bool didTextureChange = false;
switch(texture.InputType)
{
case InputType.Texture:
EditorGUI.BeginChangeCheck();
texture.Texture = (Texture2D)EditorGUI.ObjectField(textureRect, texture.Texture, typeof(Texture2D), false);
didTextureChange = EditorGUI.EndChangeCheck();
if(didTextureChange && texture.Texture != null) texture.FilterMode = texture.Texture.filterMode;
if(didTextureChange) DetermineOutputResolution(_textureSources, _imageAdjust);
texture.FilterMode = (FilterMode)EditorGUI.EnumPopup(filterRect, texture.FilterMode);
break;
case InputType.Gradient:
if(texture.GradientTexture == null) EditorGUI.DrawRect(textureRect, Color.black);
else EditorGUI.DrawPreviewTexture(textureRect, texture.GradientTexture);
if(Event.current.type == EventType.MouseDown && textureRect.Contains(Event.current.mousePosition))
{
if(texture.Gradient == null) texture.Gradient = new Gradient();
GradientEditor2.Open(texture.Gradient, (Gradient gradient, Texture2D tex) => {
texture.Gradient = gradient;
texture.GradientTexture = tex;
texture.Texture = tex;
// Needs to call these itself because it's in a callback not the OnGUI method
Pack();
Repaint();
}, texture.GradientDirection == GradientDirection.Vertical, false, _imageAdjust.Resolution, new Vector2Int(4096, 4096));
}
EditorGUI.BeginChangeCheck();
texture.GradientDirection = (GradientDirection)EditorGUI.EnumPopup(filterRect, texture.GradientDirection);
if(EditorGUI.EndChangeCheck() && texture.Gradient != null)
{
texture.GradientTexture = Converter.GradientToTexture(texture.Gradient, _imageAdjust.Resolution.x, _imageAdjust.Resolution.y, texture.GradientDirection == GradientDirection.Vertical);
texture.Texture = texture.GradientTexture;
}
break;
case InputType.Color:
EditorGUI.BeginChangeCheck();
texture.Color = EditorGUI.ColorField(textureRect, texture.Color);
if(EditorGUI.EndChangeCheck())
{
texture.Texture = Converter.ColorToTexture(texture.Color, 16, 16);
}
break;
}
_changeCheckForPacking |= EditorGUI.EndChangeCheck();
// draw 4 channl boxes on the right side
Rect rectR = new Rect(textureRect.x + textureRect.width, textureRect.y, channelWidth, channelWidth);
Rect rectG = new Rect(textureRect.x + textureRect.width, textureRect.y + channelWidth, channelWidth, channelWidth);
Rect rectB = new Rect(textureRect.x + textureRect.width, textureRect.y + channelWidth * 2, channelWidth, channelWidth);
Rect rectA = new Rect(textureRect.x + textureRect.width, textureRect.y + channelWidth * 3, channelWidth, channelWidth);
Rect rectMax = new Rect(textureRect.x + textureRect.width, textureRect.y + channelWidth * 4, channelWidth, channelWidth);
if (texture.Texture != null)
{
ChannelPreviewMaterial.SetTexture("_MainTex", texture.Texture);
ChannelPreviewMaterial.SetFloat("_Channel", 0);
EditorGUI.DrawPreviewTexture(rectR, texture.Texture, ChannelPreviewMaterial, ScaleMode.ScaleToFit);
ChannelPreviewMaterial.SetFloat("_Channel", 1);
EditorGUI.DrawPreviewTexture(rectG, texture.Texture, ChannelPreviewMaterial, ScaleMode.ScaleToFit);
ChannelPreviewMaterial.SetFloat("_Channel", 2);
EditorGUI.DrawPreviewTexture(rectB, texture.Texture, ChannelPreviewMaterial, ScaleMode.ScaleToFit);
ChannelPreviewMaterial.SetFloat("_Channel", 3);
EditorGUI.DrawPreviewTexture(rectA, texture.Texture, ChannelPreviewMaterial, ScaleMode.ScaleToFit);
ChannelPreviewMaterial.SetFloat("_Channel", 4);
EditorGUI.DrawPreviewTexture(rectMax, texture.Texture, ChannelPreviewMaterial, ScaleMode.ScaleToFit);
}else
{
EditorGUI.DrawRect(rectR, Color.black);
EditorGUI.DrawRect(rectG, Color.black);
EditorGUI.DrawRect(rectB, Color.black);
EditorGUI.DrawRect(rectA, Color.black);
EditorGUI.DrawRect(rectMax, Color.black);
}
// Draw circle button bext to each channel box
Rect circleR = new Rect(rectR.x + rectR.width + 5, rectR.y + rectR.height / 2 - 10 + 1, 40, 18);
Rect circleG = new Rect(rectG.x + rectG.width + 5, rectG.y + rectG.height / 2 - 10 + 1, 40, 18);
Rect circleB = new Rect(rectB.x + rectB.width + 5, rectB.y + rectB.height / 2 - 10 + 1, 40, 18);
Rect circleA = new Rect(rectA.x + rectA.width + 5, rectA.y + rectA.height / 2 - 10 + 1, 40, 18);
Rect circleMax = new Rect(rectMax.x + rectMax.width + 5, rectMax.y + rectMax.height / 2 - 10, 40, 18);
_positionsChannelIn[index * 5 + 0] = new Vector2(circleR.x + circleR.width, circleR.y + circleR.height / 2);
_positionsChannelIn[index * 5 + 1] = new Vector2(circleG.x + circleG.width, circleG.y + circleG.height / 2);
_positionsChannelIn[index * 5 + 2] = new Vector2(circleB.x + circleB.width, circleB.y + circleB.height / 2);
_positionsChannelIn[index * 5 + 3] = new Vector2(circleA.x + circleA.width, circleA.y + circleA.height / 2);
_positionsChannelIn[index * 5 + 4] = new Vector2(circleMax.x + circleMax.width, circleMax.y + circleMax.height / 2);
_rectsChannelIn[index * 5 + 0] = circleR;
_rectsChannelIn[index * 5 + 1] = circleG;
_rectsChannelIn[index * 5 + 2] = circleB;
_rectsChannelIn[index * 5 + 3] = circleA;
_rectsChannelIn[index * 5 + 4] = circleMax;
DrawInputChannel(circleR, index, TextureChannelIn.R);
DrawInputChannel(circleG, index, TextureChannelIn.G);
DrawInputChannel(circleB, index, TextureChannelIn.B);
DrawInputChannel(circleA, index, TextureChannelIn.A);
DrawInputChannel(circleMax, index, TextureChannelIn.Max);
return didTextureChange;
}
void DrawInputChannel(Rect position, int index, TextureChannelIn channel)
{
if(Event.current.type == EventType.MouseDown && position.Contains(Event.current.mousePosition))
{
Event.current.Use();
if (_creatingConnection == null) _creatingConnection = Connection.Create(index, channel);
else _creatingConnection.SetFrom(index, channel, this);
}
GUI.Button(position, channel.ToString());
}
bool DoFallback(TextureChannelOut channel)
{
return _connections.Any(c => c.ToChannel == channel && c.FromTextureIndex != -1
&& _textureSources[c.FromTextureIndex].Texture != null) == false;
}
Color GetColor(TextureChannelIn c)
{
switch (c)
{
case TextureChannelIn.R: return Color.red;
case TextureChannelIn.G: return Color.green;
case TextureChannelIn.B: return Color.blue;
case TextureChannelIn.A: return Color.white;
case TextureChannelIn.Max: return Color.yellow;
default: return Color.black;
}
}
Color GetColor(TextureChannelOut c)
{
switch (c)
{
case TextureChannelOut.R: return Color.red;
case TextureChannelOut.G: return Color.green;
case TextureChannelOut.B: return Color.blue;
case TextureChannelOut.A: return Color.white;
default: return Color.black;
}
}
// Packing Logic
void Pack()
{
SaveConfig();
// Update all gradient textures (incase max size changed)
Vector2Int gradientSize = _imageAdjust.Resolution;
foreach (TextureSource source in _textureSources)
{
if (source.InputType == InputType.Gradient && source.GradientTexture != null && source.GradientTexture.width != gradientSize.x && source.GradientTexture.height != gradientSize.y)
{
source.GradientTexture = Converter.GradientToTexture(source.Gradient, gradientSize.x, gradientSize.y, source.GradientDirection == GradientDirection.Vertical);
source.Texture = source.GradientTexture;
}
}
_outputTexture = Pack(_textureSources, _outputConfigs, _connections, _filterMode, _colorSpace, _imageAdjust, _kernel_x, _kernel_y, _kernel_loops, _kernel_strength, _kernel_twoPass, _kernel_grayScale, _kernel_channels);
if(OnChange != null) OnChange(_outputTexture, _textureSources, _outputConfigs, _connections.ToArray());
}
static void DetermineOutputResolution(TextureSource[] sources, ImageAdjust colorAdjust)
{
int width = 16;
int height = 16;
foreach (TextureSource source in sources)
{
source.FindMaxSize(ref width, ref height);
}
// round up to nearest power of 2
width = Mathf.NextPowerOfTwo(width);
height = Mathf.NextPowerOfTwo(height);
// clamp to max size of 4096
width = Mathf.Clamp(width, 16, 4096);
height = Mathf.Clamp(height, 16, 4096);
colorAdjust.Resolution = new Vector2Int(width, height);
}
public static Texture2D Pack(TextureSource[] sources, OutputConfig[] outputConfigs, IEnumerable<Connection> connections, FilterMode targetFilterMode, ColorSpace targetColorSpace, ImageAdjust colorAdjust = null,
float[] kernelX = null, float[] kernelY = null, int kernelLoops = 1, float kernelStrength = 1, bool kernelTwoPass = false, bool kernelGrayscale = false, bool[] kernelChannels = null)
{
if(colorAdjust == null)
{
colorAdjust = new ImageAdjust();
DetermineOutputResolution(sources, colorAdjust);
}
int width = colorAdjust.Resolution.x;
int height = colorAdjust.Resolution.y;
RenderTexture target = new RenderTexture(width, height, 24, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
target.enableRandomWrite = true;
target.filterMode = targetFilterMode;
target.Create();
ComputeShader.SetTexture(0, "Result", target);
ComputeShader.SetFloat("Width", width);
ComputeShader.SetFloat("Height", height);
ComputeShader.SetFloat("Rotation", colorAdjust.Rotation / 360f * 2f * Mathf.PI);
ComputeShader.SetVector("Scale", colorAdjust.Scale);
ComputeShader.SetVector("Offset", colorAdjust.Offset);
ComputeShader.SetFloat("Hue", colorAdjust.Hue);
ComputeShader.SetFloat("Saturation", colorAdjust.Saturation);
ComputeShader.SetFloat("Brightness", colorAdjust.Brightness);
bool repeatTextures = Math.Abs(colorAdjust.Scale.x) > 1 || Math.Abs(colorAdjust.Scale.y) > 1;
// Set Compute Shader Properties
int rCons = SetComputeValues(sources, connections, outputConfigs[0], TextureChannelOut.R, repeatTextures);
int gCons = SetComputeValues(sources, connections, outputConfigs[1], TextureChannelOut.G, repeatTextures);
int bCons = SetComputeValues(sources, connections, outputConfigs[2], TextureChannelOut.B, repeatTextures);
int aCons = SetComputeValues(sources, connections, outputConfigs[3], TextureChannelOut.A, repeatTextures);
bool hasTransparency = aCons > 0 || outputConfigs[3].Fallback < 1;
ComputeShader.Dispatch(0, width / 8 + 1, height / 8 + 1, 1);
if(kernelX != null && kernelY != null)
{
// Settings Vector4s instead of floats because the SetFloats function is broken
float[] kernelNone = GetKernelPreset(KernelPreset.None, false);
ComputeShader.SetVectorArray("Kernel_X", kernelX.Select((f,i) => new Vector4(Mathf.Lerp(kernelNone[i], f, kernelStrength), 0, 0, 0)).ToArray());
ComputeShader.SetVectorArray("Kernel_Y", kernelY.Select((f,i) => new Vector4(Mathf.Lerp(kernelNone[i], f, kernelStrength), 0, 0, 0)).ToArray());
ComputeShader.SetBool("Kernel_Grayscale", kernelGrayscale);
ComputeShader.SetBool("Kernel_TwoPass", kernelTwoPass);
ComputeShader.SetVector("Kernel_Channels", new Vector4(kernelChannels[0] ? 1 : 0, kernelChannels[1] ? 1 : 0, kernelChannels[2] ? 1 : 0, kernelChannels[3] ? 1 : 0));
// define the opposite way, because each loop flips it
RenderTexture filterTarget = target;
RenderTexture filterInput = new RenderTexture(width, height, 24, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
filterInput.enableRandomWrite = true;
filterInput.filterMode = targetFilterMode;
filterInput.Create();
for(int i = 0; i < kernelLoops; i++)
{
RenderTexture temp = filterInput;
filterInput = filterTarget;
filterTarget = temp;
ComputeShader.SetTexture(1, "Kernel_Input", filterInput);
ComputeShader.SetTexture(1, "Result", filterTarget);
ComputeShader.Dispatch(1, width / 8 + 1, height / 8 + 1, 1);
}
target = filterTarget;
}
Texture2D atlas = new Texture2D(width, height, TextureFormat.RGBA32, true, targetColorSpace == ColorSpace.Linear);
RenderTexture.active = target;
atlas.ReadPixels(new Rect(0, 0, width, height), 0, 0);
atlas.filterMode = targetFilterMode;
atlas.wrapMode = TextureWrapMode.Clamp;
atlas.alphaIsTransparency = hasTransparency;
atlas.Apply();
return atlas;
}
static int SetComputeValues(TextureSource[] sources, IEnumerable<Connection> allConnections, OutputConfig config, TextureChannelOut outChannel, bool repeatMode)
{
// Find all incoming connections
Connection[] chnlConnections = allConnections.Where(c => c.ToChannel == outChannel && sources[c.FromTextureIndex].Texture != null).ToArray();
// Set textures
for(int i = 0; i < chnlConnections.Length; i++)
{
TextureSource s = sources[chnlConnections[i].FromTextureIndex];
// set the sampler states correctly
s.UncompressedTexture.wrapMode = repeatMode ? TextureWrapMode.Repeat : TextureWrapMode.Clamp;
s.UncompressedTexture.filterMode = s.InputType == InputType.Gradient ? FilterMode.Bilinear : s.FilterMode;
ComputeShader.SetTexture(0, outChannel.ToString() + "_Input_" + i, s.UncompressedTexture);
ComputeShader.SetInt(outChannel.ToString() + "_Channel_" + i, (int)chnlConnections[i].FromChannel);
}
for(int i = chnlConnections.Length; i < 4; i++)
{
ComputeShader.SetTexture(0, outChannel.ToString() + "_Input_" + i, Texture2D.whiteTexture);
}
// Set other data
ComputeShader.SetInt(outChannel.ToString() + "_Count", chnlConnections.Length);
ComputeShader.SetInt(outChannel.ToString() + "_BlendMode", (int)config.BlendMode);
ComputeShader.SetBool(outChannel.ToString() + "_Invert", config.Invert == InvertMode.Invert);
ComputeShader.SetFloat(outChannel.ToString() + "_Fallback", config.Fallback);
return chnlConnections.Length;
}
#region Kernel
static float[] GetKernelPreset(KernelPreset preset, bool isXKernel)
{
// return a 5x5 kernel. always 25 values
switch(preset)
{
case KernelPreset.Sharpen: return new float[]{ 0,0,0,0,0, 0,0,-0.5f, 0,0, 0,-0.5f, 3, -0.5f, 0, 0,0, -0.5f, 0,0, 0,0,0,0,0 };
case KernelPreset.EdgeDetection:
if (isXKernel) return new float[]{ 0,0,0,0,0, 0,-1,0,1,0, 0,-2,0,2,0, 0,-1,0,1,0, 0,0,0,0,0 };
else return new float[]{ 0,0,0,0,0, 0,-1,-2,-1,0, 0,0,0,0,0, 0,1,2,1,0, 0,0,0,0,0 };
case KernelPreset.GaussianBlur3x3: return new float[]{ 0,0,0,0,0, 0,0.0625f,0.125f,0.0625f,0, 0,0.125f,0.25f,0.125f,0, 0,0.0625f,0.125f,0.0625f,0, 0,0,0,0,0 };
case KernelPreset.GaussianBlur5x5: return new float[]{ 0.003f, 0.0133f, 0.0219f, 0.0133f, 0.003f, 0.0133f, 0.0596f, 0.0983f, 0.0596f, 0.0133f, 0.0219f, 0.0983f, 0.1621f, 0.0983f, 0.0219f, 0.0133f, 0.0596f, 0.0983f, 0.0596f, 0.0133f, 0.003f, 0.0133f, 0.0219f, 0.0133f, 0.003f };
}
return new float[]{ 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0 };
}
void KernelPresetSetValues(KernelPreset preset)
{
_kernel_loops = 1;
_kernel_strength = 1;
_kernel_twoPass = false;
_kernel_grayScale = false;
_kernel_channels = new bool[]{ true, true, true, true };
if(preset == KernelPreset.GaussianBlur3x3 || preset == KernelPreset.GaussianBlur5x5) _kernel_loops = 10;
if(preset == KernelPreset.EdgeDetection) _kernel_twoPass = true;
if(preset == KernelPreset.EdgeDetection) _kernel_channels = new bool[]{ true, true, true, false };
}
# endregion
# region Channel Unpacker
void ExportChannel(RenderTexture renderTex, Vector4 lerpR, Vector4 lerpG, Vector4 lerpB, Vector4 lerpA , string namePostfix)
{
ComputeShader.SetVector("Channels_Strength_R", lerpR);
ComputeShader.SetVector("Channels_Strength_G", lerpG);
ComputeShader.SetVector("Channels_Strength_B", lerpB);
ComputeShader.SetVector("Channels_Strength_A", lerpA);
ComputeShader.Dispatch(2, _outputTexture.width / 8, _outputTexture.height / 8, 1);
Texture2D tex = new Texture2D(renderTex.width, renderTex.height, TextureFormat.RGBA32, true, _colorSpace == ColorSpace.Linear);
RenderTexture.active = renderTex;
tex.ReadPixels(new Rect(0, 0, renderTex.width, renderTex.height), 0, 0);
tex.filterMode = renderTex.filterMode;
tex.wrapMode = TextureWrapMode.Clamp;
tex.alphaIsTransparency = false;
tex.Apply();
Save(tex, _saveFolder, _saveName + namePostfix);
}
void ExportChannels(bool exportAsBlackAndWhite)
{
Pack();
DeterminePathAndFileNameIfEmpty();
RenderTexture target = new RenderTexture(_outputTexture.width, _outputTexture.height, 24, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
target.enableRandomWrite = true;
target.filterMode = _outputTexture.filterMode;
target.Create();
ComputeShader.SetTexture(2, "Unpacker_Input", _outputTexture);
ComputeShader.SetTexture(2, "Result", target);
Vector4 r = new Vector4(1, 0, 0, 0);
Vector4 g = new Vector4(0, 1, 0, 0);
Vector4 b = new Vector4(0, 0, 1, 0);
Vector4 a = new Vector4(0, 0, 0, 1);
Vector4 none = new Vector4(0, 0, 0, 0);
if(exportAsBlackAndWhite)
{
if(_channel_export[0])
ExportChannel(target, r, r, r, none, "_R");
if(_channel_export[1])
ExportChannel(target, g, g, g, none, "_G");
if(_channel_export[2])
ExportChannel(target, b, b, b, none, "_B");
if(_channel_export[3])
ExportChannel(target, a, a, a, none, "_A");
}
else
{
if(_channel_export[0])
ExportChannel(target, r, none, none, none, "_R");
if(_channel_export[1])
ExportChannel(target, none, g, none, none, "_G");
if(_channel_export[2])
ExportChannel(target, none, none, b, none, "_B");
if(_channel_export[3])
ExportChannel(target, none, none, none, a, "_A");
}
}
# endregion
#region Save
void Save()
{
if (_outputTexture == null) return;
DeterminePathAndFileNameIfEmpty();
string path = _saveFolder + "/" + _saveName + GetTypeEnding(_saveType);
byte[] bytes = null;
if(File.Exists(path))
{
// open dialog
if (!EditorUtility.DisplayDialog("File already exists", "Do you want to overwrite the file?", "Yes", "No"))
{
return;
}
}
switch (_saveType)
{
case SaveType.PNG: bytes = _outputTexture.EncodeToPNG(); break;
case SaveType.JPG: bytes = _outputTexture.EncodeToJPG((int)_saveQuality); break;
}
System.IO.File.WriteAllBytes(path, bytes);
AssetDatabase.Refresh();
TextureImporter importer = AssetImporter.GetAtPath(path) as TextureImporter;
importer.streamingMipmaps = true;
importer.sRGBTexture = _colorSpace == ColorSpace.Gamma;
importer.filterMode = _filterMode;
importer.alphaIsTransparency = _outputTexture.alphaIsTransparency;
importer.textureCompression = TextureImporterCompression.Compressed;
TextureImporterFormat overwriteFormat = importer.DoesSourceTextureHaveAlpha() ?
Config.Singleton.texturePackerCompressionWithAlphaOverwrite : Config.Singleton.texturePackerCompressionNoAlphaOverwrite;
if(overwriteFormat != TextureImporterFormat.Automatic)
{
importer.SetPlatformTextureSettings(new TextureImporterPlatformSettings()
{
name = "PC",
overridden = true,
maxTextureSize = 2048,
format = overwriteFormat
});
}else
{
importer.SetPlatformTextureSettings(new TextureImporterPlatformSettings()
{
name = "PC",
overridden = false,
});
}
importer.SaveAndReimport();
Texture2D tex = AssetDatabase.LoadAssetAtPath<Texture2D>(path);
if(OnSave != null) OnSave(tex);
}
void Save(Texture2D texture, string folder, string name)
{
string path = folder + "/" + name + GetTypeEnding(_saveType);
byte[] bytes = null;
if(File.Exists(path))
{
// open dialog
if (!EditorUtility.DisplayDialog("File already exists", "Do you want to overwrite the file?", "Yes", "No"))
{
return;
}
}
switch (_saveType)
{
case SaveType.PNG: bytes = texture.EncodeToPNG(); break;
case SaveType.JPG: bytes = texture.EncodeToJPG((int)_saveQuality); break;
}
System.IO.File.WriteAllBytes(path, bytes);
AssetDatabase.Refresh();
TextureImporter importer = AssetImporter.GetAtPath(path) as TextureImporter;
importer.streamingMipmaps = true;
importer.sRGBTexture = _colorSpace == ColorSpace.Gamma;
importer.filterMode = _filterMode;
importer.alphaIsTransparency = texture.alphaIsTransparency;
importer.textureCompression = TextureImporterCompression.Compressed;
TextureImporterFormat overwriteFormat = importer.DoesSourceTextureHaveAlpha() ?
Config.Singleton.texturePackerCompressionWithAlphaOverwrite : Config.Singleton.texturePackerCompressionNoAlphaOverwrite;
if(overwriteFormat != TextureImporterFormat.Automatic)
{
importer.SetPlatformTextureSettings(new TextureImporterPlatformSettings()
{
name = "PC",
overridden = true,
maxTextureSize = 2048,
format = overwriteFormat
});
}else
{
importer.SetPlatformTextureSettings(new TextureImporterPlatformSettings()
{
name = "PC",
overridden = false,
});
}
importer.SaveAndReimport();
}
void CreateConfig()
{
string path = EditorUtility.SaveFilePanelInProject("Save Texture Packer Config", "TexturePackerConfig", "asset", "Save Texture Packer Config", _saveFolder);
if (path.Length != 0)
{
_config = ScriptableObject.CreateInstance<TexturePackerConfig>();
AssetDatabase.CreateAsset(_config, path);
SaveConfig();
}
}
void SaveConfig()
{
if (_config == null) return;
_config.Sources = _textureSources;
_config.Connections = _connections.ToArray();
_config.OutputConfigs = _outputConfigs;
_config.SaveFolder = _saveFolder;
_config.SaveName = _saveName;
_config.SaveType = _saveType;
_config.SaveQuality = _saveQuality;
_config.ColorSpace = _colorSpace;
_config.FilterMode = _filterMode;
_config.ImageAdjust = _imageAdjust;
_config.KernelPreset = _kernelPreset;
_config.KernelX = _kernel_x;
_config.KernelY = _kernel_y;
_config.KernelLoops = _kernel_loops;
_config.KernelStrength = _kernel_strength;
_config.KernelChannels = _kernel_channels;
EditorUtility.SetDirty(_config);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
void LoadConfig()
{
if(_config == null) return;
_textureSources = _config.Sources;
_connections = _config.Connections.ToList();
_outputConfigs = _config.OutputConfigs;
_saveFolder = _config.SaveFolder;
_saveName = _config.SaveName;
_saveType = _config.SaveType;
_saveQuality = _config.SaveQuality;
_colorSpace = _config.ColorSpace;
_filterMode = _config.FilterMode;
_imageAdjust = _config.ImageAdjust;
_kernelPreset = _config.KernelPreset;
_kernel_x = _config.KernelX;
_kernel_y = _config.KernelY;
_kernel_loops = _config.KernelLoops;
_kernel_strength = _config.KernelStrength;
_kernel_channels = _config.KernelChannels;
// Expand sources to 4
_textureSources = _textureSources.Concat(Enumerable.Repeat(new TextureSource(), 4 - _textureSources.Length)).ToArray();
// Expand output configs to 4
_outputConfigs = _outputConfigs.Concat(Enumerable.Repeat(new OutputConfig(), 4 - _outputConfigs.Length)).ToArray();
}
#endregion
}
}