yum/3ner

A toon shader for Unity's BIRP.

git clone https://git.yummers.dev/yum/3ner

yumssfd bugfixes072fa89

master
3.9 KiB120 linesraw
1using UnityEngine;
2using UnityEditor;
3
4public class StackTextures3D : EditorWindow
5{
6    Texture2D[] _slices = new Texture2D[0];
7    SerializedObject _so;
8    SerializedProperty _slicesProp;
9    [SerializeField] bool reverseStackOrder = false;
10
11    [MenuItem("Tools/yum_food/Stack Textures to 3D")]
12    static void Open() => GetWindow<StackTextures3D>("Stack Textures to 3D");
13
14    void OnEnable()
15    {
16        _so = new SerializedObject(this);
17    }
18
19    // SerializedObject needs a backing field with SerializeField for array GUI.
20    // Unity EditorWindow supports this natively.
21    [SerializeField] Texture2D[] slices = new Texture2D[0];
22
23    void OnGUI()
24    {
25        _so.Update();
26
27        EditorGUILayout.LabelField("Stack Textures to 3D", EditorStyles.boldLabel);
28        EditorGUILayout.HelpBox(
29            "Provide an ordered array of 2D textures. Each texture becomes one depth slice. " +
30            "All textures must share the same dimensions and format.",
31            MessageType.Info);
32
33        var prop = _so.FindProperty("slices");
34        EditorGUILayout.PropertyField(prop, new GUIContent("Slices"), true);
35        EditorGUILayout.PropertyField(
36            _so.FindProperty("reverseStackOrder"),
37            new GUIContent("Reverse Stack Order"));
38        _so.ApplyModifiedProperties();
39
40        if (slices == null || slices.Length == 0)
41            return;
42
43        EditorGUILayout.Space();
44
45        // Show preview info.
46        int nullCount = 0;
47        foreach (var s in slices)
48            if (s == null) nullCount++;
49
50        if (nullCount > 0)
51            EditorGUILayout.HelpBox($"{nullCount} null slot(s) — fill all slots before generating.", MessageType.Warning);
52
53        if (nullCount == 0 && slices.Length > 0)
54        {
55            var first = slices[0];
56            EditorGUILayout.LabelField($"Resolution: {first.width} x {first.height} x {slices.Length}");
57        }
58
59        EditorGUI.BeginDisabledGroup(nullCount > 0);
60        if (GUILayout.Button("Generate"))
61            DoGenerate();
62        EditorGUI.EndDisabledGroup();
63    }
64
65    void DoGenerate()
66    {
67        if (slices.Length == 0)
68            return;
69
70        int width = slices[0].width;
71        int height = slices[0].height;
72        int depth = slices.Length;
73        TextureFormat fmt = slices[0].format;
74
75        for (int i = 1; i < depth; i++)
76        {
77            if (slices[i].width != width || slices[i].height != height)
78            {
79                EditorUtility.DisplayDialog("Error",
80                    $"Slice {i} ({slices[i].name}) is {slices[i].width}x{slices[i].height}, " +
81                    $"expected {width}x{height}.", "OK");
82                return;
83            }
84            if (slices[i].format != fmt)
85            {
86                EditorUtility.DisplayDialog("Error",
87                    $"Slice {i} ({slices[i].name}) format is {slices[i].format}, " +
88                    $"expected {fmt}.", "OK");
89                return;
90            }
91        }
92
93        bool mipMaps = false;
94        var tex = new Texture3D(width, height, depth, fmt, mipMaps);
95
96        for (int z = 0; z < depth; z++)
97        {
98            int sourceIndex = reverseStackOrder ? (depth - 1 - z) : z;
99            var pixels = slices[sourceIndex].GetPixels();
100            for (int y = 0; y < height; y++)
101            for (int x = 0; x < width; x++)
102            {
103                tex.SetPixel(x, y, z, pixels[y * width + x]);
104            }
105        }
106
107        tex.wrapMode = TextureWrapMode.Repeat;
108        tex.filterMode = FilterMode.Bilinear;
109        tex.Apply();
110
111        string dir = System.IO.Path.GetDirectoryName(AssetDatabase.GetAssetPath(slices[0]));
112        string path = $"{dir}/Stacked3D_{width}x{height}x{depth}.asset";
113        path = AssetDatabase.GenerateUniqueAssetPath(path);
114
115        AssetDatabase.CreateAsset(tex, path);
116        AssetDatabase.SaveAssets();
117        EditorGUIUtility.PingObject(tex);
118        Debug.Log($"[StackTextures3D] Saved {width}x{height}x{depth} ({fmt}) to {path}");
119    }
120}