diff options
| author | yum <yum.food.vr@gmail.com> | 2026-03-24 16:26:28 -0700 |
|---|---|---|
| committer | yum <yum.food.vr@gmail.com> | 2026-03-24 16:26:28 -0700 |
| commit | 043de4d0360c54a507b90369e1afe1d1d6ace28f (patch) | |
| tree | 32823361cbedc06b8b1f72cf84be11f5dacae8a2 /Scripts/Editor/StackTextures3D.cs | |
| parent | 86438c831074ea2b161db28c18c998eb47b3600f (diff) | |
Port ssfd feature from 2ner
Diffstat (limited to 'Scripts/Editor/StackTextures3D.cs')
| -rw-r--r-- | Scripts/Editor/StackTextures3D.cs | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/Scripts/Editor/StackTextures3D.cs b/Scripts/Editor/StackTextures3D.cs new file mode 100644 index 0000000..3e88025 --- /dev/null +++ b/Scripts/Editor/StackTextures3D.cs @@ -0,0 +1,115 @@ +using UnityEngine; +using UnityEditor; + +public class StackTextures3D : EditorWindow +{ + Texture2D[] _slices = new Texture2D[0]; + SerializedObject _so; + SerializedProperty _slicesProp; + + [MenuItem("Tools/yum_food/Stack Textures to 3D")] + static void Open() => GetWindow<StackTextures3D>("Stack Textures to 3D"); + + void OnEnable() + { + _so = new SerializedObject(this); + } + + // SerializedObject needs a backing field with SerializeField for array GUI. + // Unity EditorWindow supports this natively. + [SerializeField] Texture2D[] slices = new Texture2D[0]; + + void OnGUI() + { + _so.Update(); + + EditorGUILayout.LabelField("Stack Textures to 3D", EditorStyles.boldLabel); + EditorGUILayout.HelpBox( + "Provide an ordered array of 2D textures. Each texture becomes one depth slice. " + + "All textures must share the same dimensions and format.", + MessageType.Info); + + var prop = _so.FindProperty("slices"); + EditorGUILayout.PropertyField(prop, new GUIContent("Slices"), true); + _so.ApplyModifiedProperties(); + + if (slices == null || slices.Length == 0) + return; + + EditorGUILayout.Space(); + + // Show preview info. + int nullCount = 0; + foreach (var s in slices) + if (s == null) nullCount++; + + if (nullCount > 0) + EditorGUILayout.HelpBox($"{nullCount} null slot(s) — fill all slots before generating.", MessageType.Warning); + + if (nullCount == 0 && slices.Length > 0) + { + var first = slices[0]; + EditorGUILayout.LabelField($"Resolution: {first.width} x {first.height} x {slices.Length}"); + } + + EditorGUI.BeginDisabledGroup(nullCount > 0); + if (GUILayout.Button("Generate")) + DoGenerate(); + EditorGUI.EndDisabledGroup(); + } + + void DoGenerate() + { + if (slices.Length == 0) + return; + + int width = slices[0].width; + int height = slices[0].height; + int depth = slices.Length; + TextureFormat fmt = slices[0].format; + + for (int i = 1; i < depth; i++) + { + if (slices[i].width != width || slices[i].height != height) + { + EditorUtility.DisplayDialog("Error", + $"Slice {i} ({slices[i].name}) is {slices[i].width}x{slices[i].height}, " + + $"expected {width}x{height}.", "OK"); + return; + } + if (slices[i].format != fmt) + { + EditorUtility.DisplayDialog("Error", + $"Slice {i} ({slices[i].name}) format is {slices[i].format}, " + + $"expected {fmt}.", "OK"); + return; + } + } + + bool mipMaps = true; + var tex = new Texture3D(width, height, depth, fmt, mipMaps); + + for (int z = 0; z < depth; z++) + { + var pixels = slices[z].GetPixels(); + for (int y = 0; y < height; y++) + for (int x = 0; x < width; x++) + { + tex.SetPixel(x, y, z, pixels[y * width + x]); + } + } + + tex.wrapMode = TextureWrapMode.Repeat; + tex.filterMode = FilterMode.Trilinear; + tex.Apply(); + + string dir = System.IO.Path.GetDirectoryName(AssetDatabase.GetAssetPath(slices[0])); + string path = $"{dir}/Stacked3D_{width}x{height}x{depth}.asset"; + path = AssetDatabase.GenerateUniqueAssetPath(path); + + AssetDatabase.CreateAsset(tex, path); + AssetDatabase.SaveAssets(); + EditorGUIUtility.PingObject(tex); + Debug.Log($"[StackTextures3D] Saved {width}x{height}x{depth} ({fmt}) to {path}"); + } +} |
