Added ChannelMixer
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7d55aad869a0f394abc5a531318a7b7d
|
||||
guid: 5ade15144914b364380a9284f2fa0a63
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
@@ -0,0 +1,61 @@
|
||||
Shader "Hidden/ChannelMixer"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
_MainTex ("Red Channel Texture", 2D) = "white"
|
||||
}
|
||||
SubShader
|
||||
{
|
||||
Cull Off
|
||||
ZWrite Off
|
||||
ZTest Always
|
||||
|
||||
Pass
|
||||
{
|
||||
CGPROGRAM
|
||||
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
|
||||
#pragma shader_feature_local _HAS_TEX_R
|
||||
#pragma shader_feature_local _HAS_TEX_G
|
||||
#pragma shader_feature_local _HAS_TEX_B
|
||||
#pragma shader_feature_local _HAS_TEX_A
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
|
||||
struct appdata {
|
||||
float4 vertex : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct v2f {
|
||||
float2 uv : TEXCOORD0;
|
||||
float4 vertex : SV_POSITION;
|
||||
};
|
||||
|
||||
v2f vert(appdata v) {
|
||||
v2f o;
|
||||
o.vertex = UnityObjectToClipPos(v.vertex);
|
||||
o.uv = v.uv;
|
||||
return o;
|
||||
}
|
||||
|
||||
sampler2D _MainTex;
|
||||
int4 _ChannelSource;
|
||||
|
||||
float4 frag (v2f i) : SV_Target
|
||||
{
|
||||
float4 color = tex2D(_MainTex, i.uv);
|
||||
|
||||
float r = color[_ChannelSource.x];
|
||||
float g = color[_ChannelSource.y];
|
||||
float b = color[_ChannelSource.z];
|
||||
float a = color[_ChannelSource.w];
|
||||
|
||||
return float4(r, g, b, a);
|
||||
}
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 21d62fa27a1e5b1439a779be2e3ac586
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,33 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Misaki.ArtToolEditor
|
||||
{
|
||||
internal class ChannelMixerMenu
|
||||
{
|
||||
[MenuItem("Assets/Art Tools/Texture Helpers/Channel Mixer", true)]
|
||||
public static bool ChannelMixerValidator()
|
||||
{
|
||||
foreach (var selectedObject in Selection.objects)
|
||||
{
|
||||
if (selectedObject is not Texture2D)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[MenuItem("Assets/Art Tools/Texture Helpers/Channel Mixer")]
|
||||
public static void ChannelMixer()
|
||||
{
|
||||
var window = EditorWindow.GetWindow<OutputOptionsWindow>(true, "Channel Mixer Output Options");
|
||||
window.WithItemSource(Selection.objects);
|
||||
window.RegisterVisualProvider<ChannelMixerVisualProvider>();
|
||||
window.RegisterProcessor<ChannelMixerProcessor>();
|
||||
|
||||
window.InitializeAndShow();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fa98d95c359c67b44a90ea5568f872e2
|
||||
@@ -0,0 +1,54 @@
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Experimental.Rendering;
|
||||
|
||||
namespace Misaki.ArtToolEditor
|
||||
{
|
||||
internal class ChannelMixerProcessor : IAssetsProcessor
|
||||
{
|
||||
private static class ShaderConstants
|
||||
{
|
||||
internal const string Channel_Mixer_Shader_Path = "Hidden/ChannelMixer";
|
||||
|
||||
internal const string Main_Texture_Property = "_MainTex";
|
||||
internal const string Channel_Source_Property = "_ChannelSource";
|
||||
}
|
||||
|
||||
public TextureChannel RChannelSource = TextureChannel.R;
|
||||
public TextureChannel GChannelSource = TextureChannel.G;
|
||||
public TextureChannel BChannelSource = TextureChannel.B;
|
||||
public TextureChannel AChannelSource = TextureChannel.A;
|
||||
|
||||
public string outputNameSuffix = "_ChannelMixer";
|
||||
|
||||
public void OnPreProcess(AssetsProcessContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnProcess(Object source, string outputDirectory, AssetsProcessContext context)
|
||||
{
|
||||
if (source is not Texture2D texture)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var tempRT = RenderTexture.GetTemporary(texture.width, texture.height, 0, GraphicsFormat.R8G8B8A8_UNorm);
|
||||
var material = new Material(Shader.Find(ShaderConstants.Channel_Mixer_Shader_Path));
|
||||
|
||||
material.SetVector(ShaderConstants.Channel_Source_Property, new Vector4((int)RChannelSource, (int)GChannelSource, (int)BChannelSource, (int)AChannelSource));
|
||||
Graphics.Blit(texture, tempRT, material);
|
||||
|
||||
var texturePath = Path.Combine(outputDirectory, texture.name + outputNameSuffix + Constants.Extensions.PNG);
|
||||
TextureHelpers.ExportRenderTextureToPNG(tempRT, texturePath);
|
||||
AssetDatabase.ImportAsset(texturePath);
|
||||
|
||||
Object.DestroyImmediate(material);
|
||||
RenderTexture.ReleaseTemporary(tempRT);
|
||||
}
|
||||
|
||||
public void OnPostProcess(AssetsProcessContext context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b764f778d76e1f04f8b3e8edcd9d838d
|
||||
@@ -0,0 +1,35 @@
|
||||
using Unity.Properties;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace Misaki.ArtToolEditor
|
||||
{
|
||||
internal class ChannelMixerVisualProvider : OptionsVisualProvider
|
||||
{
|
||||
public override VisualElement ContentAfterList()
|
||||
{
|
||||
var root = new VisualElement();
|
||||
|
||||
root.Add(CreateChannelSelector("Set Channel R From", nameof(ChannelMixerProcessor.RChannelSource)));
|
||||
root.Add(CreateChannelSelector("Set Channel G From", nameof(ChannelMixerProcessor.GChannelSource)));
|
||||
root.Add(CreateChannelSelector("Set Channel B From", nameof(ChannelMixerProcessor.BChannelSource)));
|
||||
root.Add(CreateChannelSelector("Set Channel A From", nameof(ChannelMixerProcessor.AChannelSource)));
|
||||
|
||||
var nameSuffixField = new TextField("Output Name Suffix")
|
||||
{
|
||||
tooltip = "The suffix to append to the output texture name. Will overwrite original texture if set it to empty and use CurrentDirectory as output directory.",
|
||||
};
|
||||
nameSuffixField.SetBinding(nameof(TextField.value), new DataBinding() { dataSourcePath = PropertyPath.FromName(nameof(ChannelMixerProcessor.outputNameSuffix)) });
|
||||
root.Add(nameSuffixField);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
private static VisualElement CreateChannelSelector(string label, string binding)
|
||||
{
|
||||
var enumField = new EnumField(label, TextureChannel.R);
|
||||
enumField.SetBinding(nameof(EnumField.value), new DataBinding() { dataSourcePath = PropertyPath.FromName(binding) });
|
||||
|
||||
return enumField;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 21d7828e4486449479a4adaeae5970ff
|
||||
@@ -21,20 +21,20 @@ namespace Misaki.ArtToolEditor
|
||||
}
|
||||
}
|
||||
|
||||
private class Constants
|
||||
private class ShaderConstants
|
||||
{
|
||||
internal const string Generate_Mask_Shader_Path = "Hidden/GenerateMask";
|
||||
|
||||
internal const string Texture_R_Property = "_TexR";
|
||||
internal const string Texture_G_Property = "_TexG";
|
||||
internal const string Texture_B_Property = "_TexB";
|
||||
internal const string Texture_A_Property = "_TexA";
|
||||
internal const string Fallback_Property = "_Fallback";
|
||||
|
||||
internal const string Has_Texture_R_Keyword = "_HAS_TEX_R";
|
||||
internal const string Has_Texture_G_Keyword = "_HAS_TEX_G";
|
||||
internal const string Has_Texture_B_Keyword = "_HAS_TEX_B";
|
||||
internal const string Has_Texture_A_Keyword = "_HAS_TEX_A";
|
||||
|
||||
internal const string Png_Extension = ".png";
|
||||
}
|
||||
|
||||
public string metallicTextureRegex = ".*_metallic";
|
||||
@@ -49,7 +49,7 @@ namespace Misaki.ArtToolEditor
|
||||
|
||||
public string textureGroupingRegex = "^([^_]+)_";
|
||||
public string namingRegex;
|
||||
public TextureNamingSourceType namingSource;
|
||||
public TextureChannel namingSource;
|
||||
public string replaceBy = "Mask";
|
||||
|
||||
public void OnPreProcess(AssetsProcessContext context)
|
||||
@@ -100,10 +100,13 @@ namespace Misaki.ArtToolEditor
|
||||
|
||||
GetTextureSizeAndName(metallicTexture, aoTexture, detailMaskTexture, out var textureSize, out var textureName);
|
||||
SetupAndBlitTexture(metallicTexture, aoTexture, detailMaskTexture, smoothnessTexture, textureSize, out var tempRT, out var material);
|
||||
ExportTextureToPNG(group, textureName, tempRT);
|
||||
|
||||
var texturePath = Path.Combine(group.Value.outputDirectory, textureName + Constants.Extensions.PNG);
|
||||
TextureHelpers.ExportRenderTextureToPNG(tempRT, texturePath);
|
||||
AssetDatabase.ImportAsset(texturePath);
|
||||
|
||||
UnityEngine.Object.DestroyImmediate(material);
|
||||
tempRT.Release();
|
||||
RenderTexture.ReleaseTemporary(tempRT);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -138,19 +141,19 @@ namespace Misaki.ArtToolEditor
|
||||
textureName = string.Empty;
|
||||
switch (namingSource)
|
||||
{
|
||||
case TextureNamingSourceType.R:
|
||||
case TextureChannel.R:
|
||||
textureSize = new Vector2Int(metallicTexture.width, metallicTexture.height);
|
||||
textureName = Regex.Replace(metallicTexture.name, namingRegex, replaceBy, RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
break;
|
||||
case TextureNamingSourceType.G:
|
||||
case TextureChannel.G:
|
||||
textureSize = new Vector2Int(aoTexture.width, aoTexture.height);
|
||||
textureName = Regex.Replace(aoTexture.name, namingRegex, replaceBy, RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
break;
|
||||
case TextureNamingSourceType.B:
|
||||
case TextureChannel.B:
|
||||
textureSize = new Vector2Int(detailMaskTexture.width, detailMaskTexture.height);
|
||||
textureName = Regex.Replace(detailMaskTexture.name, namingRegex, replaceBy, RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
break;
|
||||
case TextureNamingSourceType.A:
|
||||
case TextureChannel.A:
|
||||
textureSize = new Vector2Int(detailMaskTexture.width, detailMaskTexture.height);
|
||||
textureName = Regex.Replace(detailMaskTexture.name, namingRegex, replaceBy, RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
break;
|
||||
@@ -162,29 +165,29 @@ namespace Misaki.ArtToolEditor
|
||||
tempRT = RenderTexture.GetTemporary(textureSize.x, textureSize.y, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
|
||||
tempRT.Create();
|
||||
|
||||
material = new Material(Shader.Find(Constants.Generate_Mask_Shader_Path));
|
||||
material = new Material(Shader.Find(ShaderConstants.Generate_Mask_Shader_Path));
|
||||
if (textureR != null)
|
||||
{
|
||||
material.SetTexture(Constants.Texture_R_Property, textureR);
|
||||
material.EnableKeyword(Constants.Has_Texture_R_Keyword);
|
||||
material.SetTexture(ShaderConstants.Texture_R_Property, textureR);
|
||||
material.EnableKeyword(ShaderConstants.Has_Texture_R_Keyword);
|
||||
}
|
||||
|
||||
if (textureG != null)
|
||||
{
|
||||
material.SetTexture(Constants.Texture_G_Property, textureG);
|
||||
material.EnableKeyword(Constants.Has_Texture_G_Keyword);
|
||||
material.SetTexture(ShaderConstants.Texture_G_Property, textureG);
|
||||
material.EnableKeyword(ShaderConstants.Has_Texture_G_Keyword);
|
||||
}
|
||||
|
||||
if (textureB != null)
|
||||
{
|
||||
material.SetTexture(Constants.Texture_B_Property, textureB);
|
||||
material.EnableKeyword(Constants.Has_Texture_B_Keyword);
|
||||
material.SetTexture(ShaderConstants.Texture_B_Property, textureB);
|
||||
material.EnableKeyword(ShaderConstants.Has_Texture_B_Keyword);
|
||||
}
|
||||
|
||||
if (textureA != null)
|
||||
{
|
||||
material.SetTexture(Constants.Texture_A_Property, textureA);
|
||||
material.EnableKeyword(Constants.Has_Texture_A_Keyword);
|
||||
material.SetTexture(ShaderConstants.Texture_A_Property, textureA);
|
||||
material.EnableKeyword(ShaderConstants.Has_Texture_A_Keyword);
|
||||
}
|
||||
|
||||
var fallback = new Vector4(
|
||||
@@ -198,20 +201,6 @@ namespace Misaki.ArtToolEditor
|
||||
Graphics.Blit(null, tempRT, material);
|
||||
}
|
||||
|
||||
private static void ExportTextureToPNG(KeyValuePair<string, PackingConfig> group, string textureName, RenderTexture tempRT)
|
||||
{
|
||||
var exportTexture = new Texture2D(tempRT.width, tempRT.height, TextureFormat.RGBA32, false);
|
||||
exportTexture.ReadPixels(new Rect(0, 0, tempRT.width, tempRT.height), 0, 0);
|
||||
exportTexture.Apply();
|
||||
var bytes = exportTexture.EncodeToPNG();
|
||||
var path = Path.Combine(group.Value.outputDirectory, textureName + Constants.Png_Extension);
|
||||
File.WriteAllBytes(path, bytes);
|
||||
|
||||
UnityEngine.Object.DestroyImmediate(exportTexture);
|
||||
|
||||
AssetDatabase.ImportAsset(path);
|
||||
}
|
||||
|
||||
private float GetFallbackValue(TextureFallbackType type)
|
||||
{
|
||||
return type switch
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Misaki.ArtToolEditor
|
||||
{
|
||||
internal class GenerateMaskVisualProvider : OptionsVisualProvider
|
||||
{
|
||||
internal override VisualElement ContentAfterList()
|
||||
public override VisualElement ContentAfterList()
|
||||
{
|
||||
var root = new VisualElement();
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Misaki.ArtToolEditor
|
||||
root.Add(CreateTextInputWithEnumField("Naming Regex",
|
||||
nameof(GenerateMaskProcessor.namingRegex),
|
||||
nameof(GenerateMaskProcessor.namingSource),
|
||||
TextureNamingSourceType.R));
|
||||
TextureChannel.R));
|
||||
|
||||
root.Add(CreateTextInputField("Replace By",
|
||||
nameof(GenerateMaskProcessor.replaceBy)));
|
||||
@@ -43,14 +43,14 @@ namespace Misaki.ArtToolEditor
|
||||
return root;
|
||||
}
|
||||
|
||||
private VisualElement CreateTextInputField(string label, string binding)
|
||||
private static VisualElement CreateTextInputField(string label, string binding)
|
||||
{
|
||||
var textField = new TextField(label);
|
||||
textField.SetBinding(nameof(TextField.value), new DataBinding() { dataSourcePath = PropertyPath.FromName(binding) });
|
||||
return textField;
|
||||
}
|
||||
|
||||
private VisualElement CreateTextInputWithEnumField(string label, string textInputBinding, string enumBinding, Enum defaultEnumValue)
|
||||
private static VisualElement CreateTextInputWithEnumField(string label, string textInputBinding, string enumBinding, Enum defaultEnumValue)
|
||||
{
|
||||
var root = new VisualElement()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user