├── Editor.meta ├── Editor ├── TexShuffle.shader ├── TexShuffle.shader.meta ├── TexturePacker.cs ├── TexturePacker.cs.meta ├── UI.meta ├── UI │ ├── channel.uxml │ ├── channel.uxml.meta │ ├── header.uxml │ ├── header.uxml.meta │ ├── index.uxml │ ├── index.uxml.meta │ ├── input.uxml │ ├── input.uxml.meta │ ├── style.uss │ ├── style.uss.meta │ ├── textureItem.uxml │ └── textureItem.uxml.meta ├── jansm.texturepacker.asmdef └── jansm.texturepacker.asmdef.meta ├── README.md ├── README.md.meta ├── package.json └── package.json.meta /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ba52789e558684c459912ce3c3f17e5b 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/TexShuffle.shader: -------------------------------------------------------------------------------- 1 | Shader "Hidden/TexShuffle" 2 | { 3 | 4 | Properties 5 | { 6 | //channels red = 0, green = 1, blue = 2, alpha = 3 7 | _Slot0Tex("R Slot source texture", 2D) = "white"{} 8 | _Slot0Channel("R Slot channel ", float) = 0 9 | 10 | _Slot1Tex ("G Slot source texture", 2D) = "white"{} 11 | _Slot1Channel("G Slot Channel", float) = 0 12 | 13 | _Slot2Tex("B Slot source texture", 2D) = "white"{} 14 | _Slot2Channel("B Slot channel", float) = 0 15 | 16 | _Slot3Tex("A Slot source texture", 2D) = "white"{} 17 | _Slot3Channel("A Slot channel", float) = 0 18 | 19 | _Inverts("Invert states", Vector) = (0,0,0,0) 20 | 21 | } 22 | 23 | SubShader 24 | { 25 | Tags { "RenderType" = "Opaque" } 26 | LOD 100 27 | 28 | Pass 29 | { 30 | CGPROGRAM 31 | 32 | #pragma editor_sync_compilation 33 | 34 | #pragma vertex vert 35 | #pragma fragment frag 36 | 37 | #include "UnityCG.cginc" 38 | 39 | struct v2f 40 | { 41 | float2 uv : TEXCOORD0; 42 | float4 pos : SV_POSITION; 43 | }; 44 | 45 | struct appdata 46 | { 47 | float4 pos : POSITION; 48 | float2 uv : TEXCOORD0; 49 | }; 50 | 51 | sampler2D _Slot0Tex, _Slot1Tex, _Slot2Tex, _Slot3Tex; 52 | float _Slot0Channel, _Slot1Channel, _Slot2Channel, _Slot3Channel; 53 | float4 _Inverts; 54 | 55 | v2f vert(appdata v) 56 | { 57 | v2f o; 58 | o.pos = UnityObjectToClipPos(v.pos); 59 | o.uv = v.uv; 60 | return o; 61 | } 62 | float GetChannel(sampler2D tex, float channelNum, float2 coord) 63 | { 64 | float value = 0; 65 | if (channelNum == 0) { value = tex2D(tex, coord).r; } 66 | else if (channelNum == 1) { value = tex2D(tex, coord).g; } 67 | else if (channelNum == 2) { value = tex2D(tex, coord).b; } 68 | else if (channelNum == 3) { value = tex2D(tex, coord).a; } 69 | 70 | return value; 71 | } 72 | 73 | float4 frag(v2f i) : SV_Target 74 | { 75 | float4 col = float4(0,0,0,0); 76 | 77 | col.r = abs(_Inverts.r - GetChannel(_Slot0Tex, _Slot0Channel, i.uv)); 78 | col.g = abs(_Inverts.g - GetChannel(_Slot1Tex, _Slot1Channel, i.uv)); 79 | col.b = abs(_Inverts.b - GetChannel(_Slot2Tex, _Slot2Channel, i.uv)); 80 | col.a = abs(_Inverts.a - GetChannel(_Slot3Tex, _Slot3Channel, i.uv)); 81 | 82 | return col; 83 | } 84 | ENDCG 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /Editor/TexShuffle.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 183ebcfa994db1542942a9ecab69a340 3 | ShaderImporter: 4 | externalObjects: {} 5 | defaultTextures: [] 6 | nonModifiableTextures: [] 7 | preprocessorOverride: 0 8 | userData: 9 | assetBundleName: 10 | assetBundleVariant: 11 | -------------------------------------------------------------------------------- /Editor/TexturePacker.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEditor; 3 | using UnityEngine; 4 | using UnityEngine.Rendering; 5 | using UnityEditor.UIElements; 6 | using UnityEngine.UIElements; 7 | using System.IO; 8 | using System.Collections; 9 | using System.Collections.Generic; 10 | 11 | public class TexturePacker : EditorWindow 12 | { 13 | VisualElement root; 14 | 15 | TextureSettings outputTextureSettings = new TextureSettings(); 16 | Label ToolLabel; 17 | readonly string classQ = "texture-item-container"; 18 | 19 | Hashtable channelModes = new Hashtable() 20 | { 21 | {"ChannelR", "texture" }, 22 | {"ChannelG", "texture" }, 23 | {"ChannelB", "texture" }, 24 | {"ChannelA", "texture" } 25 | }; 26 | 27 | Hashtable InvertStates = new Hashtable() 28 | { 29 | {"ChannelR", false }, 30 | {"ChannelG", false }, 31 | {"ChannelB", false }, 32 | {"ChannelA", false } 33 | }; 34 | 35 | 36 | enum Channels { R, G, B, A } 37 | struct TextureSettings 38 | { 39 | public int resolution; 40 | public string name; 41 | } 42 | struct Slot 43 | { 44 | public Slot(Texture2D _tex, Channels _chan) 45 | { 46 | texture = _tex; 47 | channelDestination = _chan; 48 | invert = false; 49 | } 50 | public Slot(Texture2D _tex, Channels _chan, bool _inv) 51 | { 52 | texture = _tex; 53 | channelDestination = _chan; 54 | invert = _inv; 55 | } 56 | public Texture2D texture { get; } 57 | public Channels channelDestination { get; } 58 | public bool invert { get; } 59 | } 60 | 61 | [MenuItem("Window/Texture Packer")] 62 | public static void ShowWindow() 63 | { 64 | var window = GetWindow(); 65 | window.titleContent = new GUIContent("Texture Packer"); 66 | window.maxSize = new Vector2(400, 190); 67 | window.minSize = new Vector2(400, 190); 68 | } 69 | 70 | private void OnEnable() 71 | { 72 | root = rootVisualElement; 73 | root.styleSheets.Add(AssetDatabase.LoadAssetAtPath("Packages/com.jansm.texturepacker/Editor/UI/style.uss")); 74 | var UXMLTree = AssetDatabase.LoadAssetAtPath("Packages/com.jansm.texturepacker/Editor/UI/index.uxml"); 75 | UXMLTree.CloneTree(root); 76 | 77 | outputTextureSettings.name = "TextureName"; 78 | 79 | outputTextureSettings.resolution = 1024; 80 | 81 | //Set ObjectField filters to Texture2D. Need to move this to UXML 82 | root.Query().ForEach((ObjectField a) => a.objectType = typeof(Texture2D)); 83 | 84 | 85 | //TODO interactively add resolutions 86 | var ResToolbar = root.Q(); 87 | ResToolbar.Add(ToolLabel = new Label() { text = outputTextureSettings.resolution.ToString() }); 88 | ResToolbar.menu.AppendAction("256", (DropdownMenuAction a) => { ToolLabel.text = a.name; outputTextureSettings.resolution = 256; }); 89 | ResToolbar.menu.AppendAction("512", (DropdownMenuAction a) => { ToolLabel.text = a.name; outputTextureSettings.resolution = 512; }); 90 | ResToolbar.menu.AppendAction("1024", (DropdownMenuAction a) => { ToolLabel.text = a.name; outputTextureSettings.resolution = 1024; }); 91 | ResToolbar.menu.AppendAction("2048", (DropdownMenuAction a) => { ToolLabel.text = a.name; outputTextureSettings.resolution = 2048; }); 92 | ResToolbar.menu.AppendAction("4096", (DropdownMenuAction a) => { ToolLabel.text = a.name; outputTextureSettings.resolution = 4096; }); 93 | ResToolbar.menu.AppendAction("8192", (DropdownMenuAction a) => { ToolLabel.text = a.name; outputTextureSettings.resolution = 8192; }); 94 | 95 | root.Query("ToolbarMode") 96 | .ForEach((ToolbarMenu t) => 97 | { 98 | t.menu.AppendAction("Texture", (DropdownMenuAction a) => 99 | { 100 | channelModes[t.parent.parent.name] = "texture"; 101 | t.parent.parent.Q().AddToClassList("hidden"); 102 | t.parent.parent.Q("TextureInputObject").RemoveFromClassList("hidden"); 103 | t.parent.parent.Q("TextureChannels").RemoveFromClassList("hidden"); 104 | }); 105 | t.menu.AppendAction("Color", (DropdownMenuAction a) => 106 | { 107 | channelModes[t.parent.parent.name] = "color"; 108 | t.parent.parent.Q().RemoveFromClassList("hidden"); 109 | t.parent.parent.Q("TextureInputObject").AddToClassList("hidden"); 110 | t.parent.parent.Q("TextureChannels").AddToClassList("hidden"); 111 | }); 112 | }); 113 | 114 | root.Query("SelectChannelMenu") 115 | .ForEach((ToolbarMenu t) => 116 | { 117 | Label SelectChannelLabel; 118 | t.Add(SelectChannelLabel = new Label() { text = "R", name = "ChannelLabel" }); 119 | t.menu.AppendAction("R", (DropdownMenuAction a) => { SelectChannelLabel.text = a.name; }); 120 | t.menu.AppendAction("G", (DropdownMenuAction a) => { SelectChannelLabel.text = a.name; }); 121 | t.menu.AppendAction("B", (DropdownMenuAction a) => { SelectChannelLabel.text = a.name; }); 122 | t.menu.AppendAction("A", (DropdownMenuAction a) => { SelectChannelLabel.text = a.name; }); 123 | }); 124 | 125 | root.Query().ForEach((ToolbarToggle t) => { 126 | t.RegisterCallback>((ChangeEvent e) => 127 | { 128 | InvertStates[t.parent.Q().name] = e.newValue; 129 | 130 | }); 131 | }); 132 | 133 | root.Q