426 lines
18 KiB
C#
426 lines
18 KiB
C#
#if UNITY_EDITOR
|
|
|
|
using UnityEngine;
|
|
using UnityEditor;
|
|
using System.Collections.Generic;
|
|
|
|
namespace MK.TextureChannelPacker
|
|
{
|
|
public class MKTextureChannelPacker : EditorWindow
|
|
{
|
|
private GUIStyle flowTextStyle { get { return new GUIStyle(EditorStyles.label) { wordWrap = true }; } }
|
|
|
|
private static readonly string _defaultFilePath = "Assets/_MK/MKTextureChannelPacker";
|
|
private string _filePath = _defaultFilePath;
|
|
private string _filename = "NewTexture";
|
|
|
|
private Vector2 _scrollPos = Vector2.zero;
|
|
private TextureFormat _outputFormat = TextureFormat.PNG;
|
|
private int _outputWidth = 1024, _outputHeight = 1024;
|
|
|
|
private enum TextureFormat
|
|
{
|
|
PNG = 0
|
|
#if UNITY_2018_3_OR_NEWER
|
|
,
|
|
TGA = 1
|
|
#endif
|
|
};
|
|
|
|
private enum TextureChannel
|
|
{
|
|
Red = 0,
|
|
Green = 1,
|
|
Blue = 2,
|
|
Alpha = 3
|
|
};
|
|
|
|
private enum ChannelColor
|
|
{
|
|
Black = 0,
|
|
White = 1
|
|
};
|
|
|
|
private Texture2D _sourceTexture0 = null;
|
|
private TextureChannel _sourceChannel0 = TextureChannel.Red;
|
|
private TextureChannel _targetChannel0 = TextureChannel.Red;
|
|
private ChannelColor _targetChannelColor0 = ChannelColor.Black;
|
|
private Texture2D _sourceTexture1 = null;
|
|
private TextureChannel _sourceChannel1 = TextureChannel.Green;
|
|
private TextureChannel _targetChannel1 = TextureChannel.Green;
|
|
private ChannelColor _targetChannelColor1 = ChannelColor.Black;
|
|
private Texture2D _sourceTexture2 = null;
|
|
private TextureChannel _sourceChannel2 = TextureChannel.Blue;
|
|
private TextureChannel _targetChannel2 = TextureChannel.Blue;
|
|
private ChannelColor _targetChannelColor2 = ChannelColor.Black;
|
|
private Texture2D _sourceTexture3 = null;
|
|
private TextureChannel _sourceChannel3 = TextureChannel.Alpha;
|
|
private TextureChannel _targetChannel3 = TextureChannel.Alpha;
|
|
private ChannelColor _targetChannelColor3 = ChannelColor.Black;
|
|
|
|
[MenuItem("Window/MK/Texture Channel Packer")]
|
|
static void Init()
|
|
{
|
|
MKTextureChannelPacker window = (MKTextureChannelPacker)EditorWindow.GetWindow<MKTextureChannelPacker>(true, "MK Texture Channel Packer", true);
|
|
window.maxSize = new Vector2(360, 480);
|
|
window.minSize = new Vector2(360, 480);
|
|
window.Show();
|
|
}
|
|
|
|
private void OnGUI()
|
|
{
|
|
_scrollPos = EditorGUILayout.BeginScrollView(_scrollPos, false, true);
|
|
EditorGUILayout.LabelField("V 0.1");
|
|
EditorGUILayout.LabelField("Pack different color channels of input textures and create a new texture. The Aspect Ratio of the input should match!", flowTextStyle);
|
|
Divider();
|
|
|
|
EditorGUILayout.LabelField("How to use:", UnityEditor.EditorStyles.boldLabel);
|
|
EditorGUILayout.LabelField("1. Select your input textures.");
|
|
EditorGUILayout.LabelField("2. Select the input channels you want to pack.");
|
|
EditorGUILayout.LabelField("3. Select the target channels where you want to output.");
|
|
EditorGUILayout.LabelField("4. Run the operation via \"Create Texture\".", flowTextStyle);
|
|
EditorGUILayout.LabelField("Existing files will be overwritten!", UnityEditor.EditorStyles.boldLabel);
|
|
|
|
Divider();
|
|
EditorGUILayout.LabelField("Input:", UnityEditor.EditorStyles.boldLabel);
|
|
PackerPopup(ref _sourceTexture0, ref _sourceChannel0, ref _targetChannel0, ref _targetChannelColor0);
|
|
PackerPopup(ref _sourceTexture1, ref _sourceChannel1, ref _targetChannel1, ref _targetChannelColor1);
|
|
PackerPopup(ref _sourceTexture2, ref _sourceChannel2, ref _targetChannel2, ref _targetChannelColor2);
|
|
PackerPopup(ref _sourceTexture3, ref _sourceChannel3, ref _targetChannel3, ref _targetChannelColor3);
|
|
Divider();
|
|
|
|
EditorGUILayout.LabelField("Output Settings:", UnityEditor.EditorStyles.boldLabel);
|
|
_outputFormat = (TextureFormat)EditorGUILayout.EnumPopup("Format", _outputFormat);
|
|
EditorGUILayout.BeginHorizontal();
|
|
float labelWidthBase = EditorGUIUtility.labelWidth;
|
|
EditorGUIUtility.labelWidth = 37;
|
|
_outputWidth = EditorGUILayout.IntField("Width", _outputWidth);
|
|
EditorGUIUtility.labelWidth = 42;
|
|
_outputHeight = EditorGUILayout.IntField("Height", _outputHeight);
|
|
EditorGUIUtility.labelWidth = labelWidthBase;
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
_filePath = EditorGUILayout.TextField("Output Folder", _filePath);
|
|
if(GUILayout.Button("Select"))
|
|
_filePath = EditorUtility.SaveFolderPanel("Output Folder", _filePath, "");
|
|
if(_filePath == "")
|
|
_filePath = _defaultFilePath;
|
|
EditorGUILayout.EndHorizontal();
|
|
_filename = EditorGUILayout.TextField("Filename", _filename);
|
|
|
|
Divider();
|
|
|
|
EditorGUILayout.EndScrollView();
|
|
|
|
bool oversized = _outputWidth >= 16384 || _outputHeight >= 16384;
|
|
if(oversized)
|
|
{
|
|
EditorGUILayout.LabelField("Texture width or height is oversized.", UnityEditor.EditorStyles.boldLabel);
|
|
}
|
|
|
|
bool doubledChannelUsed = false;
|
|
if(CheckDoubledOutputChannel(_targetChannel0) || CheckDoubledOutputChannel(_targetChannel1) ||
|
|
CheckDoubledOutputChannel(_targetChannel2) || CheckDoubledOutputChannel(_targetChannel3))
|
|
{
|
|
doubledChannelUsed = true;
|
|
EditorGUILayout.LabelField("Every output channel has to be unique.", UnityEditor.EditorStyles.boldLabel);
|
|
}
|
|
|
|
if(!doubledChannelUsed && !oversized && _filename != "")
|
|
{
|
|
if(GUILayout.Button("Create Texture"))
|
|
{
|
|
EditorUtility.DisplayProgressBar("Texture progress", "Creating new texture...", 0.5f);
|
|
Texture2D _outputTexture = new Texture2D(_outputWidth, _outputHeight);
|
|
|
|
bool ra0 = false, ra1 = false, ra2 = false, ra3 = false;
|
|
|
|
if(_sourceTexture0)
|
|
{
|
|
ra0 = SetTextureReadable(_sourceTexture0);
|
|
}
|
|
if(_sourceTexture1)
|
|
{
|
|
ra1 = SetTextureReadable(_sourceTexture1);
|
|
}
|
|
if(_sourceTexture2)
|
|
{
|
|
ra2 = SetTextureReadable(_sourceTexture2);
|
|
}
|
|
if(_sourceTexture3)
|
|
{
|
|
ra3 = SetTextureReadable(_sourceTexture3);
|
|
}
|
|
|
|
for(int i = 0; i < _outputHeight; i++)
|
|
{
|
|
Vector2 pPos = new Vector2();
|
|
pPos.y = (float)i / (float)_outputHeight;
|
|
|
|
for(int j = 0; j < _outputWidth; j++)
|
|
{
|
|
pPos.x = (float)j / (float)_outputWidth;
|
|
Color pixelColor = Color.black, sourceRead0, sourceRead1, sourceRead2, sourceRead3;
|
|
|
|
sourceRead0 = ReadPixelValue(_sourceTexture0, pPos, _targetChannelColor0);
|
|
sourceRead1 = ReadPixelValue(_sourceTexture1, pPos, _targetChannelColor1);
|
|
sourceRead2 = ReadPixelValue(_sourceTexture2, pPos, _targetChannelColor2);
|
|
sourceRead3 = ReadPixelValue(_sourceTexture3, pPos, _targetChannelColor3);
|
|
|
|
PackColorChannelValueIntoChannel(sourceRead0, _sourceChannel0, _targetChannel0, ref pixelColor);
|
|
PackColorChannelValueIntoChannel(sourceRead1, _sourceChannel1, _targetChannel1, ref pixelColor);
|
|
PackColorChannelValueIntoChannel(sourceRead2, _sourceChannel2, _targetChannel2, ref pixelColor);
|
|
PackColorChannelValueIntoChannel(sourceRead3, _sourceChannel3, _targetChannel3, ref pixelColor);
|
|
|
|
_outputTexture.SetPixel(j, i, pixelColor);
|
|
}
|
|
}
|
|
_outputTexture.Apply();
|
|
SaveOutputTexture(_outputTexture, _filePath, FilterFilename(_filename), _outputFormat, _outputWidth >= _outputHeight ? _outputWidth : _outputHeight);
|
|
|
|
if(_sourceTexture0)
|
|
SetTextureReadable(_sourceTexture0, ra0);
|
|
if(_sourceTexture1)
|
|
SetTextureReadable(_sourceTexture1, ra1);
|
|
if(_sourceTexture2)
|
|
SetTextureReadable(_sourceTexture2, ra2);
|
|
if(_sourceTexture3)
|
|
SetTextureReadable(_sourceTexture3, ra3);
|
|
|
|
EditorUtility.ClearProgressBar();
|
|
}
|
|
}
|
|
}
|
|
|
|
private static bool SetTextureReadable(Texture2D texture, bool setReadable = true)
|
|
{
|
|
string texturePath = AssetDatabase.GetAssetPath(texture);
|
|
TextureImporter textureImporter = (TextureImporter)TextureImporter.GetAtPath(texturePath);
|
|
bool isReadable = textureImporter.isReadable;
|
|
textureImporter.isReadable = setReadable ? true : false;
|
|
AssetDatabase.ImportAsset(texturePath);
|
|
AssetDatabase.Refresh();
|
|
|
|
return isReadable;
|
|
}
|
|
|
|
private static void PackColorChannelValueIntoChannel(Color source, TextureChannel sourceChannel, TextureChannel targetChannel, ref Color color)
|
|
{
|
|
switch(sourceChannel)
|
|
{
|
|
case TextureChannel.Alpha:
|
|
switch(targetChannel)
|
|
{
|
|
case TextureChannel.Alpha:
|
|
color.a = source.a;
|
|
break;
|
|
case TextureChannel.Blue:
|
|
color.b = source.a;
|
|
break;
|
|
case TextureChannel.Green:
|
|
color.g = source.a;
|
|
break;
|
|
default:
|
|
color.r = source.a;
|
|
break;
|
|
}
|
|
break;
|
|
case TextureChannel.Blue:
|
|
switch(targetChannel)
|
|
{
|
|
case TextureChannel.Alpha:
|
|
color.a = source.b;
|
|
break;
|
|
case TextureChannel.Blue:
|
|
color.b = source.b;
|
|
break;
|
|
case TextureChannel.Green:
|
|
color.g = source.b;
|
|
break;
|
|
default:
|
|
color.r = source.b;
|
|
break;
|
|
}
|
|
break;
|
|
case TextureChannel.Green:
|
|
switch(targetChannel)
|
|
{
|
|
case TextureChannel.Alpha:
|
|
color.a = source.g;
|
|
break;
|
|
case TextureChannel.Blue:
|
|
color.b = source.g;
|
|
break;
|
|
case TextureChannel.Green:
|
|
color.g = source.g;
|
|
break;
|
|
default:
|
|
color.r = source.g;
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
switch(targetChannel)
|
|
{
|
|
case TextureChannel.Alpha:
|
|
color.a = source.r;
|
|
break;
|
|
case TextureChannel.Blue:
|
|
color.b = source.r;
|
|
break;
|
|
case TextureChannel.Green:
|
|
color.g = source.r;
|
|
break;
|
|
default:
|
|
color.r = source.r;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
private static Color ReadPixelValue(Texture2D texture, Vector2 pPos, ChannelColor channelColor)
|
|
{
|
|
if(texture != null)
|
|
{
|
|
int width, height;
|
|
width = Mathf.RoundToInt((float)texture.width * pPos.x);
|
|
height = Mathf.RoundToInt((float)texture.height * pPos.y);
|
|
|
|
return texture.GetPixel(width, height);
|
|
}
|
|
else
|
|
{
|
|
Color color;
|
|
|
|
switch(channelColor)
|
|
{
|
|
case ChannelColor.Black:
|
|
color = new Color(0,0,0,1);
|
|
break;
|
|
case ChannelColor.White:
|
|
color = new Color(1,1,1,1);
|
|
break;
|
|
default:
|
|
color = new Color(0,0,0,1);
|
|
break;
|
|
}
|
|
|
|
return color;
|
|
}
|
|
}
|
|
|
|
private static void PackerPopup(ref Texture2D texture, ref TextureChannel sourceChannel, ref TextureChannel targetChannel, ref ChannelColor targetChannelColor)
|
|
{
|
|
EditorGUILayout.BeginHorizontal();
|
|
EditorGUILayout.BeginVertical();
|
|
texture = (Texture2D) EditorGUILayout.ObjectField(texture, typeof(Texture2D), false, GUILayout.Width(70), GUILayout.Height(70));
|
|
EditorGUILayout.EndVertical();
|
|
if(texture != null)
|
|
{
|
|
EditorGUILayout.BeginVertical();
|
|
EditorGUILayout.LabelField("From:", UnityEditor.EditorStyles.boldLabel, GUILayout.Width(70));
|
|
sourceChannel = (TextureChannel) EditorGUILayout.EnumPopup(sourceChannel, GUILayout.Width(70));
|
|
EditorGUILayout.EndVertical();
|
|
EditorGUILayout.BeginVertical();
|
|
EditorGUILayout.LabelField("To:", UnityEditor.EditorStyles.boldLabel, GUILayout.Width(70));
|
|
targetChannel = (TextureChannel) EditorGUILayout.EnumPopup(targetChannel, GUILayout.Width(70));
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
else
|
|
{
|
|
EditorGUILayout.BeginVertical();
|
|
EditorGUILayout.LabelField("Assume:", UnityEditor.EditorStyles.boldLabel, GUILayout.Width(70));
|
|
targetChannelColor = (ChannelColor) EditorGUILayout.EnumPopup(targetChannelColor, GUILayout.Width(70));
|
|
EditorGUILayout.EndVertical();
|
|
EditorGUILayout.BeginVertical();
|
|
EditorGUILayout.LabelField("From:", UnityEditor.EditorStyles.boldLabel, GUILayout.Width(70));
|
|
sourceChannel = (TextureChannel) EditorGUILayout.EnumPopup(sourceChannel, GUILayout.Width(70));
|
|
EditorGUILayout.EndVertical();
|
|
EditorGUILayout.BeginVertical();
|
|
EditorGUILayout.LabelField("To:", UnityEditor.EditorStyles.boldLabel, GUILayout.Width(70));
|
|
targetChannel = (TextureChannel) EditorGUILayout.EnumPopup(targetChannel, GUILayout.Width(70));
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
EditorGUILayout.EndHorizontal();
|
|
}
|
|
|
|
private bool CheckDoubledOutputChannel(TextureChannel channel)
|
|
{
|
|
int channelUsageCount = 0;
|
|
if(channel == _targetChannel0)
|
|
channelUsageCount++;
|
|
if(channel == _targetChannel1)
|
|
channelUsageCount++;
|
|
if(channel == _targetChannel2)
|
|
channelUsageCount++;
|
|
if(channel == _targetChannel3)
|
|
channelUsageCount++;
|
|
|
|
if(channelUsageCount > 1)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
private static void Divider()
|
|
{
|
|
GUILayout.Box("", new GUILayoutOption[] { GUILayout.ExpandWidth(true), GUILayout.Height(2) });
|
|
}
|
|
|
|
private static string FilterFilename(string name)
|
|
{
|
|
List<char> notAllowedFilenameChars = new List<char>(System.IO.Path.GetInvalidFileNameChars());
|
|
List<char> filename = new List<char>();
|
|
|
|
foreach(char c in name)
|
|
{
|
|
if(!notAllowedFilenameChars.Contains(c))
|
|
filename.Add(c);
|
|
}
|
|
|
|
return new string(filename.ToArray());
|
|
}
|
|
|
|
private static void SaveOutputTexture(Texture2D tex, string path, string name, TextureFormat format, int maxSize)
|
|
{
|
|
path = path.Substring(path.IndexOf("Assets"));
|
|
path += "/";
|
|
|
|
string fileSuffix = "tex";
|
|
#if UNITY_2018_3_OR_NEWER
|
|
if(format == TextureFormat.PNG)
|
|
fileSuffix = ".png";
|
|
else
|
|
fileSuffix = ".tga";
|
|
#endif
|
|
|
|
path = path + name + fileSuffix;
|
|
|
|
#if UNITY_2018_3_OR_NEWER
|
|
if(format == TextureFormat.PNG)
|
|
System.IO.File.WriteAllBytes(path, tex.EncodeToPNG());
|
|
else
|
|
System.IO.File.WriteAllBytes(path, tex.EncodeToTGA());
|
|
#endif
|
|
|
|
AssetDatabase.Refresh();
|
|
|
|
TextureImporter textureImporter = (TextureImporter)TextureImporter.GetAtPath(path);
|
|
textureImporter.wrapMode = TextureWrapMode.Repeat;
|
|
textureImporter.maxTextureSize = maxSize;
|
|
textureImporter.alphaSource = TextureImporterAlphaSource.None;
|
|
textureImporter.textureCompression = TextureImporterCompression.CompressedHQ;
|
|
textureImporter.mipmapEnabled = true;
|
|
|
|
AssetDatabase.SaveAssets();
|
|
AssetDatabase.ImportAsset(path);
|
|
AssetDatabase.Refresh();
|
|
EditorUtility.FocusProjectWindow();
|
|
Selection.activeObject = AssetDatabase.LoadAssetAtPath(path, typeof(UnityEngine.Object));
|
|
EditorGUIUtility.PingObject(Selection.activeObject);
|
|
}
|
|
}
|
|
}
|
|
#endif |