#if UNITY_EDITOR using System; using System.IO; using System.Linq; using UnityEditor; using UnityEngine; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering; using Object = UnityEngine.Object; namespace AllIn1VfxToolkit { public class AllIn1VfxWindow : EditorWindow { private const string Version = "2.1"; [MenuItem("Tools/AllIn1/VfxToolkitWindow")] public static void ShowAllIn1VfxToolkitWindowWindow() => GetWindow("All In 1 VFX Toolkit Window"); public static readonly string CUSTOM_EDITOR_HEADER = "AllIn1VfxCustomEditorImage"; private static string basePath = "Assets/Plugins/AllIn1VfxToolkit"; public static readonly string materialsSavesPath = "/MaterialSaves"; public static readonly string particlePresetsSavesPath = "/ParticlePresets"; public static readonly string renderImagesSavesPath = "/Demo & Assets/Textures"; public static readonly string normalMapSavesPath = "/Demo & Assets/Textures/Distortion Normal Maps"; public static readonly string gradientSavesPath = "/Demo & Assets/Textures/Color Gradients"; public static readonly string noiseSavesPath = "/Demo & Assets/Textures/Noise"; public static readonly string atlasSavesPath = "/Demo & Assets/Textures/Shapes"; public static readonly string materialAutoSetupSavesPath = "/Demo & Assets/Demo/Materials"; private Vector2 scrollPosition = Vector2.zero; private DefaultAsset materialTargetFolder = null; private GUIStyle style, bigLabel = new GUIStyle(); private const int BigFontSize = 16; private Texture2D imageInspector; private const int BUTTON_WIDTH = 600; private enum TextureSizes { _2 = 2, _4 = 4, _8 = 8, _16 = 16, _32 = 32, _64 = 64, _128 = 128, _256 = 256, _512 = 512, _1024 = 1024, _2048 = 2048 } private TextureSizes gradientSizes = TextureSizes._128; [SerializeField] private Gradient gradient = new Gradient(); private FilterMode gradientFiltering = FilterMode.Bilinear; private TextureSizes atlasSizesX = TextureSizes._512; private TextureSizes atlasSizesY = TextureSizes._512; private FilterMode atlasFiltering = FilterMode.Bilinear; private Texture2D targetNormalImage; private float normalStrength = 5f; private int normalSmoothing = 1; private int isComputingNormals = 0; private int currTab = 0; private Texture2D editorTex, editorTexInput, cleanEditorTex; private void OnGUI() { style = new GUIStyle(EditorStyles.helpBox); style.margin = new RectOffset(0, 0, 0, 0); bigLabel = new GUIStyle(EditorStyles.boldLabel); bigLabel.fontSize = BigFontSize; using(var scrollView = new EditorGUILayout.ScrollViewScope(scrollPosition, GUILayout.Width(position.width), GUILayout.Height(position.height))) { scrollPosition = scrollView.scrollPosition; if(imageInspector == null) imageInspector = GetInspectorImage(); if(imageInspector) { Rect rect = EditorGUILayout.GetControlRect(GUILayout.Height(50)); GUI.DrawTexture(rect, imageInspector, ScaleMode.ScaleToFit, true); } DrawLine(Color.grey, 1, 3); currTab = GUILayout.Toolbar(currTab, new string[] {"Save Paths", "Texture Editor", "Texture Creators", "Other"}); DrawLine(Color.grey, 1, 3); if(currTab == 0) { SavePaths(); } else if(currTab == 1) { TextureEditor(); } else if(currTab == 2) { NormalMapCreator(); DrawLine(Color.grey, 1, 3); GradientCreator(); DrawLine(Color.grey, 1, 3); TextureAtlasPacker(); DrawLine(Color.grey, 1, 3); NoiseCreator(); } else { OtherTab(); } GUILayout.Space(10); DrawLine(Color.grey, 1, 3); GUILayout.Label("Current asset version is " + Version, EditorStyles.boldLabel); } } public static Texture2D GetInspectorImage() { string[] guids = AssetDatabase.FindAssets($"{CUSTOM_EDITOR_HEADER} t:texture"); if(guids.Length > 0) { string path = AssetDatabase.GUIDToAssetPath(guids[0]); return AssetDatabase.LoadAssetAtPath(path); } return null; } private void SavePaths() { GUILayout.Label("Material Save Path", bigLabel); GUILayout.Space(20); GUILayout.Label("Select the folder where new Materials will be saved when the Save Material To Folder button of the asset component is pressed", EditorStyles.boldLabel); HandleSaveFolderEditorPref("All1VfxMaterials", basePath + materialsSavesPath, "Material"); DrawLine(Color.grey, 1, 3); GUILayout.Label("Particle Presets Save Path", bigLabel); GUILayout.Space(20); GUILayout.Label("Select the folder where new Particle Helper Preset or Particle System Preset is saved with the Particle Helper Component", EditorStyles.boldLabel); HandleSaveFolderEditorPref("All1VfxParticlePresets", basePath + particlePresetsSavesPath, "Presets"); DrawLine(Color.grey, 1, 3); GUILayout.Label("Render Material to Image Save Path", bigLabel); GUILayout.Space(20); EditorGUILayout.BeginHorizontal(); { float scaleSlider = 1; if(PlayerPrefs.HasKey("All1VfxRenderImagesScale")) scaleSlider = PlayerPrefs.GetFloat("All1VfxRenderImagesScale"); GUILayout.Label("Rendered Image Texture Scale", GUILayout.MaxWidth(190)); scaleSlider = EditorGUILayout.Slider(scaleSlider, 0.2f, 5f, GUILayout.MaxWidth(200)); if(GUILayout.Button("Default Value", GUILayout.MaxWidth(100))) PlayerPrefs.SetFloat("All1VfxRenderImagesScale", 1f); else PlayerPrefs.SetFloat("All1VfxRenderImagesScale", scaleSlider); } EditorGUILayout.EndHorizontal(); GUILayout.Label("Select the folder where new Images will be saved when the Render Material To Image button of the asset component is pressed", EditorStyles.boldLabel); HandleSaveFolderEditorPref("All1VfxRenderImages", basePath + renderImagesSavesPath, "Images"); } private void HandleSaveFolderEditorPref(string keyName, string defaultPath, string logsFeatureName) { if (!PlayerPrefs.HasKey(keyName)) PlayerPrefs.SetString(keyName, defaultPath); materialTargetFolder = (DefaultAsset)AssetDatabase.LoadAssetAtPath(PlayerPrefs.GetString(keyName), typeof(DefaultAsset)); if (materialTargetFolder == null) { PlayerPrefs.SetString(keyName, defaultPath); materialTargetFolder = (DefaultAsset)AssetDatabase.LoadAssetAtPath(PlayerPrefs.GetString(keyName), typeof(DefaultAsset)); if (materialTargetFolder == null) { materialTargetFolder = (DefaultAsset)AssetDatabase.LoadAssetAtPath("Assets/", typeof(DefaultAsset)); if(materialTargetFolder == null) { EditorGUILayout.BeginHorizontal(GUILayout.MaxWidth(600)); EditorGUILayout.HelpBox("Folder is invalid, please select a valid one", MessageType.Error, true); EditorGUILayout.EndHorizontal(); } else PlayerPrefs.SetString("Assets/", defaultPath); } } materialTargetFolder = (DefaultAsset)EditorGUILayout.ObjectField("New " + logsFeatureName + " Folder", materialTargetFolder, typeof(DefaultAsset), false, GUILayout.MaxWidth(500)); if (materialTargetFolder != null && IsAssetAFolder(materialTargetFolder)) { string path = AssetDatabase.GetAssetPath(materialTargetFolder); PlayerPrefs.SetString(keyName, path); EditorGUILayout.BeginHorizontal(GUILayout.MaxWidth(600)); EditorGUILayout.HelpBox("Valid folder! " + logsFeatureName + " save path: " + path, MessageType.Info); EditorGUILayout.EndHorizontal(); } else { EditorGUILayout.BeginHorizontal(GUILayout.MaxWidth(600)); EditorGUILayout.HelpBox("Select the new " + logsFeatureName + " Folder", MessageType.Warning, true); EditorGUILayout.EndHorizontal(); } } private void NormalMapCreator() { GUILayout.Label("Normal/Distortion Map Creator", bigLabel); GUILayout.Space(20); GUILayout.Label("Select the folder where new Normal Maps will be saved when the Create Normal Map button of the asset component is pressed", EditorStyles.boldLabel); GUILayout.Label("*These Normal Maps can then be used with the Screen Distortion effect", EditorStyles.boldLabel); HandleSaveFolderEditorPref("All1VfxNormals", basePath + normalMapSavesPath, "Normal Maps"); GUILayout.Space(20); GUILayout.Label("Assign a texture you want to create a normal map from. Choose the normal map settings and press the 'Create And Save Normal Map' button", EditorStyles.boldLabel); targetNormalImage = (Texture2D) EditorGUILayout.ObjectField("Target Image", targetNormalImage, typeof(Texture2D), false, GUILayout.MaxWidth(225)); EditorGUILayout.BeginHorizontal(); { GUILayout.Label("Normal Strength:", GUILayout.MaxWidth(150)); normalStrength = EditorGUILayout.Slider(normalStrength, 1f, 20f, GUILayout.MaxWidth(400)); } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); { GUILayout.Label("Normal Smoothing:", GUILayout.MaxWidth(150)); normalSmoothing = EditorGUILayout.IntSlider(normalSmoothing, 0, 3, GUILayout.MaxWidth(400)); } EditorGUILayout.EndHorizontal(); if(isComputingNormals == 0) { if(targetNormalImage != null) { if(GUILayout.Button("Create And Save Normal Map", GUILayout.MaxWidth(BUTTON_WIDTH))) { isComputingNormals = 1; return; } } else { GUILayout.Label("Add a Target Image to use this feature", EditorStyles.boldLabel); } } else { GUILayout.Label("Normal Map is currently being created, be patient", EditorStyles.boldLabel, GUILayout.Height(40)); Repaint(); isComputingNormals++; if(isComputingNormals > 5) { SetTextureReadWrite(AssetDatabase.GetAssetPath(targetNormalImage), true); Texture2D normalToSave = CreateNormalMap(targetNormalImage, normalStrength, normalSmoothing); string prefSavedPath = PlayerPrefs.GetString("All1VfxNormals") + "/"; string path = prefSavedPath + "NormalMap.png"; if(System.IO.File.Exists(path)) path = GetNewValidPath(path); string texName = path.Replace(prefSavedPath, ""); path = EditorUtility.SaveFilePanel("Save texture as PNG", prefSavedPath, texName, "png"); if(path.Length != 0) { byte[] pngData = normalToSave.EncodeToPNG(); if(pngData != null) File.WriteAllBytes(path, pngData); AssetDatabase.Refresh(); if(path.IndexOf("Assets/") >= 0) { string subPath = path.Substring(path.IndexOf("Assets/")); TextureImporter importer = AssetImporter.GetAtPath(subPath) as TextureImporter; if(importer != null) { SceneViewNotificationAndLog("Normal Map saved inside the project: " + subPath); importer.filterMode = FilterMode.Bilinear; importer.textureType = TextureImporterType.NormalMap; importer.wrapMode = TextureWrapMode.Repeat; importer.SaveAndReimport(); EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath(subPath, typeof(Texture))); } } else SceneViewNotificationAndLog("Normal Map saved outside the project: " + path); } isComputingNormals = 0; } } GUILayout.Label("*This process will freeze the editor for some seconds, larger images will take longer", EditorStyles.boldLabel); } private static void SetTextureReadWrite(string assetPath, bool enable) { TextureImporter tImporter = AssetImporter.GetAtPath(assetPath) as TextureImporter; if(tImporter != null) { tImporter.isReadable = enable; tImporter.SaveAndReimport(); } } private void GradientCreator() { GUILayout.Label("Color Gradient Creator", bigLabel); GUILayout.Space(20); GUILayout.Label("This feature can be used to create textures for the Color Ramp Effect", EditorStyles.boldLabel); EditorGUILayout.GradientField("Color Gradient: ", gradient, GUILayout.Height(25), GUILayout.MaxWidth(BUTTON_WIDTH)); EditorGUILayout.BeginHorizontal(); { GUILayout.Label("Texture Size:", GUILayout.MaxWidth(145)); gradientSizes = (TextureSizes) EditorGUILayout.EnumPopup(gradientSizes, GUILayout.MaxWidth(200)); } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); { GUILayout.Label("New Textures Filtering: ", GUILayout.MaxWidth(145)); gradientFiltering = (FilterMode) EditorGUILayout.EnumPopup(gradientFiltering, GUILayout.MaxWidth(200)); } EditorGUILayout.EndHorizontal(); int textureSize = (int) gradientSizes; Texture2D gradTex = new Texture2D(textureSize, 1, TextureFormat.RGBA32, false); for(int i = 0; i < textureSize; i++) gradTex.SetPixel(i, 0, gradient.Evaluate((float) i / (float) textureSize)); gradTex.Apply(); GUILayout.Space(20); GUILayout.Label("Select the folder where new Color Gradient Textures will be saved", EditorStyles.boldLabel); HandleSaveFolderEditorPref("All1VfxGradients", basePath + gradientSavesPath, "Gradients"); string prefSavedPath = PlayerPrefs.GetString("All1VfxGradients") + "/"; if(Directory.Exists(prefSavedPath)) { if(GUILayout.Button("Save Color Gradient Texture", GUILayout.MaxWidth(BUTTON_WIDTH))) { string path = prefSavedPath + "ColorGradient.png"; if(System.IO.File.Exists(path)) path = GetNewValidPath(path); string texName = path.Replace(prefSavedPath, ""); path = EditorUtility.SaveFilePanel("Save texture as PNG", prefSavedPath, texName, "png"); if(path.Length != 0) { byte[] pngData = gradTex.EncodeToPNG(); if(pngData != null) File.WriteAllBytes(path, pngData); AssetDatabase.Refresh(); if(path.IndexOf("Assets/") >= 0) { string subPath = path.Substring(path.IndexOf("Assets/")); TextureImporter importer = AssetImporter.GetAtPath(subPath) as TextureImporter; if(importer != null) { SceneViewNotificationAndLog("Gradient saved inside the project: " + subPath); importer.filterMode = gradientFiltering; importer.SaveAndReimport(); EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath(subPath, typeof(Texture))); } } else SceneViewNotificationAndLog("Gradient saved outside the project: " + path); } } } } public Texture2D[] Atlas = Array.Empty(); private int atlasXCount = 1; private int atlasYCount = 1; private bool squareAtlas = true; private void TextureAtlasPacker() { GUILayout.Label("Texture Atlas / Spritesheet Packer", bigLabel); GUILayout.Space(20); GUILayout.Label("Add Textures to the Atlas array", EditorStyles.boldLabel); ScriptableObject target = this; SerializedObject so = new SerializedObject(target); SerializedProperty stringsProperty = so.FindProperty("Atlas"); EditorGUILayout.PropertyField(stringsProperty, true, GUILayout.MaxWidth(200)); so.ApplyModifiedProperties(); squareAtlas = EditorGUILayout.Toggle("Square Atlas?", squareAtlas, GUILayout.MaxWidth(200)); EditorGUILayout.BeginHorizontal(); { if(squareAtlas) { atlasXCount = EditorGUILayout.IntSlider("Column and Row Count", atlasXCount, 1, 8, GUILayout.MaxWidth(302)); atlasYCount = atlasXCount; } else { atlasXCount = EditorGUILayout.IntSlider("Column Count", atlasXCount, 1, 8, GUILayout.MaxWidth(302)); GUILayout.Space(10); atlasYCount = EditorGUILayout.IntSlider("Row Count", atlasYCount, 1, 8, GUILayout.MaxWidth(302)); } } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); { if(squareAtlas) { GUILayout.Label("Atlas Size:", GUILayout.MaxWidth(100)); atlasSizesX = (TextureSizes) EditorGUILayout.EnumPopup(atlasSizesX, GUILayout.MaxWidth(200)); atlasSizesY = atlasSizesX; } else { GUILayout.Label("Atlas Size X:", GUILayout.MaxWidth(100)); atlasSizesX = (TextureSizes) EditorGUILayout.EnumPopup(atlasSizesX, GUILayout.MaxWidth(200)); GUILayout.Space(10); GUILayout.Label("Atlas Size Y:", GUILayout.MaxWidth(100)); atlasSizesY = (TextureSizes) EditorGUILayout.EnumPopup(atlasSizesY, GUILayout.MaxWidth(200)); } } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); { GUILayout.Label("Atlas Filtering: ", GUILayout.MaxWidth(100)); atlasFiltering = (FilterMode) EditorGUILayout.EnumPopup(atlasFiltering, GUILayout.MaxWidth(200)); } EditorGUILayout.EndHorizontal(); int atlasElements = atlasXCount * atlasYCount; int atlasWidth = (int) atlasSizesX; int atlasHeight = (int) atlasSizesY; GUILayout.Label("Output will be a " + atlasXCount + " X " + atlasYCount + " atlas, " + atlasElements + " elements in total. In a " + atlasWidth + "pixels X " + atlasHeight + "pixels texture", EditorStyles.boldLabel); int usedAtlasSlots = 0; for(int i = 0; i < Atlas.Length; i++) if(Atlas[i] != null) usedAtlasSlots++; if(usedAtlasSlots > atlasElements) GUILayout.Label("*Please reduce the Atlas texture slots by " + Mathf.Abs(atlasElements - Atlas.Length) + " (extra textures will be ignored)", EditorStyles.boldLabel); if(atlasElements > usedAtlasSlots) GUILayout.Label("*" + (atlasElements - usedAtlasSlots) + " atlas slots unused or null (it will be filled with black)", EditorStyles.boldLabel); GUILayout.Space(20); GUILayout.Label("Select the folder where new Atlases will be saved", EditorStyles.boldLabel); HandleSaveFolderEditorPref("All1VfxAtlas", basePath + atlasSavesPath, "Atlas"); string prefSavedPath = PlayerPrefs.GetString("All1VfxAtlas") + "/"; if(Directory.Exists(prefSavedPath)) { if(GUILayout.Button("Create And Save Atlas Texture", GUILayout.MaxWidth(BUTTON_WIDTH))) { string path = prefSavedPath + "Atlas.png"; if(System.IO.File.Exists(path)) path = GetNewValidPath(path); string texName = path.Replace(prefSavedPath, ""); path = EditorUtility.SaveFilePanel("Save texture as PNG", prefSavedPath, texName, "png"); if(path.Length != 0) { Texture2D[] AtlasCopy = (Texture2D[]) Atlas.Clone(); int textureXTargetWidth = atlasWidth / atlasXCount; int textureYTargetHeight = atlasHeight / atlasYCount; Texture2D newAtlas = new Texture2D(atlasWidth, atlasHeight); for(int i = 0; i < atlasYCount; i++) { for(int j = 0; j < atlasXCount; j++) { int currIndex = (i * atlasXCount) + j; bool hasImageForThisIndex = currIndex < AtlasCopy.Length && AtlasCopy[currIndex] != null; if(hasImageForThisIndex) { SetTextureReadWrite(AssetDatabase.GetAssetPath(AtlasCopy[currIndex]), true); Texture2D copyTexture = new Texture2D(AtlasCopy[currIndex].width, AtlasCopy[currIndex].height); copyTexture.SetPixels(AtlasCopy[currIndex].GetPixels()); copyTexture.Apply(); AtlasCopy[currIndex] = copyTexture; AtlasCopy[currIndex] = ScaleTexture(AtlasCopy[currIndex], textureXTargetWidth, textureYTargetHeight); AtlasCopy[currIndex].Apply(); } for(int y = 0; y < textureYTargetHeight; y++) { for(int x = 0; x < textureXTargetWidth; x++) { if(hasImageForThisIndex) newAtlas.SetPixel((j * textureXTargetWidth) + x, (i * textureYTargetHeight) + y, AtlasCopy[currIndex].GetPixel(x, y)); else newAtlas.SetPixel((j * textureXTargetWidth) + x, (i * textureYTargetHeight) + y, new Color(0, 0, 0, 1)); } } } } newAtlas.Apply(); byte[] pngData = newAtlas.EncodeToPNG(); if(pngData != null) File.WriteAllBytes(path, pngData); AssetDatabase.Refresh(); if(path.IndexOf("Assets/") >= 0) { string subPath = path.Substring(path.IndexOf("Assets/")); TextureImporter importer = AssetImporter.GetAtPath(subPath) as TextureImporter; if(importer != null) { SceneViewNotificationAndLog("Atlas saved inside the project: " + subPath); importer.filterMode = atlasFiltering; importer.SaveAndReimport(); EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath(subPath, typeof(Texture))); } } else SceneViewNotificationAndLog("Atlas saved outside the project: " + path); } } } } private Texture2D noisePreview = null; RenderTexture noiseRenderTarget = null; Material noiseMaterial; private float noiseScaleX = 10f, noiseScaleY = 10f, noiseContrast = 1f, noiseBrightness = 0f; private float noiseFractalAmount = 1f, noiseJitter = 1f; private int noiseSeed = 0; private bool noiseSquareScale = false, noiseInverted = false, isFractalNoise; private void CheckCreationNoiseTextures() { if(noisePreview == null) noisePreview = new Texture2D(256, 256); if(noiseRenderTarget == null) noiseRenderTarget = new RenderTexture(noisePreview.width, noisePreview.height, 0, RenderTextureFormat.ARGB32); } private void NoiseSetMaterial() { if(noiseType == NoiseTypes.Fractal || noiseType == NoiseTypes.Perlin || noiseType == NoiseTypes.Billow) { isFractalNoise = true; noiseMaterial = new Material(FindShader("AllIn1VfxFractalNoise")); noiseScaleX = 4f; noiseScaleY = 4f; } else { isFractalNoise = false; noiseMaterial = new Material(FindShader("AllIn1VfxWorleyNoise")); noiseScaleX = 10f; noiseScaleY = 10f; } switch(noiseType) { case NoiseTypes.Fractal: noiseFractalAmount = 8f; noiseMaterial.SetFloat("_Fractal", 1); break; case NoiseTypes.Perlin: noiseFractalAmount = 1f; noiseMaterial.SetFloat("_Fractal", 1); break; case NoiseTypes.Billow: noiseFractalAmount = 4f; noiseMaterial.SetFloat("_Fractal", 0); break; case NoiseTypes.Voronoi: noiseMaterial.SetFloat("_NoiseType", 0f); break; case NoiseTypes.Water: noiseMaterial.SetFloat("_NoiseType", 3f); break; case NoiseTypes.Cellular: noiseMaterial.SetFloat("_NoiseType", 4f); break; case NoiseTypes.Cells1: noiseMaterial.SetFloat("_NoiseType", 1f); break; case NoiseTypes.Cells2: noiseMaterial.SetFloat("_NoiseType", 2f); break; } } private TextureSizes noiseSize = TextureSizes._512; private FilterMode noiseFiltering = FilterMode.Bilinear; private enum NoiseTypes { Fractal, Perlin, Billow, Voronoi, Water, Cellular, Cells1, Cells2 } private NoiseTypes noiseType = NoiseTypes.Fractal; private void NoiseCreator() { GUILayout.Label("Tileable Noise Creator", bigLabel); GUILayout.Space(20); EditorGUILayout.BeginHorizontal(); { EditorGUILayout.BeginVertical(GUILayout.MaxWidth(550)); { if(noisePreview == null) GUILayout.Label("*Change a property to start editing a Noise texture", EditorStyles.boldLabel); EditorGUI.BeginChangeCheck(); EditorGUILayout.BeginHorizontal(); { GUILayout.Label("Noise Type:", GUILayout.MaxWidth(145)); noiseType = (NoiseTypes) EditorGUILayout.EnumPopup(noiseType, GUILayout.MaxWidth(200)); } EditorGUILayout.EndHorizontal(); if(EditorGUI.EndChangeCheck()) { NoiseSetMaterial(); CheckCreationNoiseTextures(); UpdateNoiseMatAndRender(); } EditorGUI.BeginChangeCheck(); if(isFractalNoise) { TextureEditorFloatParameter("Scale X", ref noiseScaleX, 0.1f, 50f, 4f); if(!noiseSquareScale) TextureEditorFloatParameter("Scale Y", ref noiseScaleY, 0.1f, 50f, 4f); } else { TextureEditorFloatParameter("Scale X", ref noiseScaleX, 0.1f, 50f, 10f); if(!noiseSquareScale) TextureEditorFloatParameter("Scale Y", ref noiseScaleY, 0.1f, 50f, 10f); } noiseSquareScale = EditorGUILayout.Toggle("Square Scale?", noiseSquareScale, GUILayout.MaxWidth(200)); if(noiseSquareScale) noiseScaleY = noiseScaleX; if(noiseType == NoiseTypes.Fractal) TextureEditorFloatParameter("Fractal Amount", ref noiseFractalAmount, 1f, 10f, 8f); else if(noiseType == NoiseTypes.Perlin) TextureEditorFloatParameter("Fractal Amount", ref noiseFractalAmount, 1f, 10f, 1f); else if(noiseType == NoiseTypes.Billow) TextureEditorFloatParameter("Fractal Amount", ref noiseFractalAmount, 1f, 10f, 4f); else TextureEditorFloatParameter("Jitter", ref noiseJitter, 0.0f, 2f, 1f); TextureEditorFloatParameter("Contrast", ref noiseContrast, 0.1f, 10f, 1f); TextureEditorFloatParameter("Brightness", ref noiseBrightness, -1f, 1f, 0f); TextureEditorIntParameter("Random Seed", ref noiseSeed, 0, 100, 0); noiseInverted = EditorGUILayout.Toggle("Inverted?", noiseInverted); if(EditorGUI.EndChangeCheck()) { if(noiseMaterial == null) NoiseSetMaterial(); CheckCreationNoiseTextures(); UpdateNoiseMatAndRender(); } GUILayout.Space(20); EditorGUILayout.BeginHorizontal(); { GUILayout.Label("Noise Size:", GUILayout.MaxWidth(145)); noiseSize = (TextureSizes) EditorGUILayout.EnumPopup(noiseSize, GUILayout.MaxWidth(200)); } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); { GUILayout.Label("New Noise Filtering: ", GUILayout.MaxWidth(145)); noiseFiltering = (FilterMode) EditorGUILayout.EnumPopup(noiseFiltering, GUILayout.MaxWidth(200)); } EditorGUILayout.EndHorizontal(); } EditorGUILayout.EndVertical(); if(noisePreview != null) GUILayout.Label(noisePreview, GUILayout.MaxWidth(450), GUILayout.MaxHeight(450)); } EditorGUILayout.EndHorizontal(); GUILayout.Space(20); GUILayout.Label("Select the folder where new Noise Textures will be saved", EditorStyles.boldLabel); HandleSaveFolderEditorPref("All1VfxNoise", basePath + noiseSavesPath, "Noises"); string prefSavedPath = PlayerPrefs.GetString("All1VfxNoise") + "/"; if(Directory.Exists(prefSavedPath) && noisePreview != null) { if(GUILayout.Button("Save Noise Texture", GUILayout.MaxWidth(BUTTON_WIDTH))) { string path = prefSavedPath + "Noise.png"; if(System.IO.File.Exists(path)) path = GetNewValidPath(path); string texName = path.Replace(prefSavedPath, ""); path = EditorUtility.SaveFilePanel("Save texture as PNG", prefSavedPath, texName, "png"); if(path.Length != 0) { int texSize = (int)noiseSize; Texture2D finalNoiseTex = new Texture2D(texSize, texSize); RenderTexture finalRenderTarget = new RenderTexture(finalNoiseTex.width, finalNoiseTex.height, 0, RenderTextureFormat.ARGB32); Graphics.Blit(finalNoiseTex, finalRenderTarget, noiseMaterial); finalNoiseTex.ReadPixels(new Rect(0, 0, finalRenderTarget.width, finalRenderTarget.height), 0, 0); finalNoiseTex.Apply(); byte[] pngData = finalNoiseTex.EncodeToPNG(); if(pngData != null) File.WriteAllBytes(path, pngData); AssetDatabase.Refresh(); if(path.IndexOf("Assets/") >= 0) { string subPath = path.Substring(path.IndexOf("Assets/")); TextureImporter importer = AssetImporter.GetAtPath(subPath) as TextureImporter; if(importer != null) { SceneViewNotificationAndLog("Noise saved inside the project: " + subPath); importer.filterMode = noiseFiltering; importer.SaveAndReimport(); EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath(subPath, typeof(Texture))); } } else SceneViewNotificationAndLog("Noise saved outside the project: " + path); } } } } private void UpdateNoiseMatAndRender() { if(noiseType == NoiseTypes.Fractal || noiseType == NoiseTypes.Perlin || noiseType == NoiseTypes.Billow) { noiseMaterial.SetFloat("_EndBand", noiseFractalAmount); } else noiseMaterial.SetFloat("_Jitter", noiseJitter); noiseMaterial.SetFloat("_ScaleX", noiseScaleX); noiseMaterial.SetFloat("_ScaleY", noiseScaleY); noiseMaterial.SetFloat("_Offset", (float) noiseSeed); noiseMaterial.SetFloat("_Contrast", noiseContrast); noiseMaterial.SetFloat("_Brightness", noiseBrightness); noiseMaterial.SetFloat("_Invert", noiseInverted ? 1f : 0f); Graphics.Blit(noisePreview, noiseRenderTarget, noiseMaterial); noisePreview.ReadPixels(new Rect(0, 0, noiseRenderTarget.width, noiseRenderTarget.height), 0, 0); noisePreview.Apply(); } private void OtherTab() { AutoSetupMaterialsToCurrentPipelineArea(); GUILayout.Space(10); DrawLine(Color.grey, 1, 3); GUILayout.Space(10); DisableDepthAndSceneColorEffectsArea(); GUILayout.Space(10); DrawLine(Color.grey, 1, 3); GUILayout.Space(10); SceneNotificationsToggle(); DrawLine(Color.grey, 1, 3); GUILayout.Space(10); RefreshLitShader(); } private void AutoSetupMaterialsToCurrentPipelineArea() { GUILayout.Label("AllIn1Vfx Materials Shader Auto Setup", bigLabel); GUILayout.Space(20); GUILayout.Label("Select the folder where the target AllIn1Vfx materials are contained", EditorStyles.boldLabel); HandleSaveFolderEditorPref("All1VfxAutoSetup", basePath + materialAutoSetupSavesPath, "Auto Setup"); GUILayout.Space(20); if(GUILayout.Button("Auto Setup Shaders for Materials in selected folder", GUILayout.MaxWidth(BUTTON_WIDTH))) { string autoSetupPath = PlayerPrefs.GetString("All1VfxAutoSetup"); SceneViewNotificationAndLog("Starting Material Auto Setup at: " + autoSetupPath); string[] filePaths = System.IO.Directory.GetFiles(autoSetupPath); bool isSuccess = true; Material lastTargetMat = null; if(filePaths != null && filePaths.Length > 0) { for(int i = 0; i < filePaths.Length; i++) { Object obj = UnityEditor.AssetDatabase.LoadAssetAtPath(filePaths[i], typeof(Material)); if(obj is Material mat) { lastTargetMat = mat; string shaderName = mat.shader.name; if(shaderName.Contains("AllIn1Vfx/")) { shaderName = shaderName.Replace("AllIn1Vfx/", ""); if(shaderName.Contains("AllIn1Vfx")) //Means it is a variation of the asset main shader { isSuccess &= SetShaderBasedOnEffectsAndPipeline(mat); } } else if(shaderName.Contains("Hidden/InternalError")) //If the material is broken we'll override it by a AllIn1Vfx one { isSuccess &= SetShaderBasedOnEffectsAndPipeline(mat); } } } if(!isSuccess){ string targetShader = GetTargetShaderName(lastTargetMat); EditorUtility.DisplayDialog("Missing Shader", $"Shader {targetShader} not found. Import the appropriate Pipeline package as explained in the Documentation first section", "Ok"); } } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); if(isSuccess) SceneViewNotificationAndLog("Material Auto Setup finished"); } } private void DisableDepthAndSceneColorEffectsArea() { GUILayout.Label("Disable Depth And Scene Color effects from materials", bigLabel); GUILayout.Label("These effects are: Soft Particles, Intersection Glow, Screen Distortion"); GUILayout.Space(20); GUILayout.Label("Select the folder where the target AllIn1Vfx materials are contained", EditorStyles.boldLabel); HandleSaveFolderEditorPref("DepthAndSceneColorPath", basePath + materialAutoSetupSavesPath, "Effects Disable"); GUILayout.Space(20); if(GUILayout.Button("Disable Depth and Scene Color Effects for Materials in selected folder", GUILayout.MaxWidth(BUTTON_WIDTH))) { string autoSetupPath = PlayerPrefs.GetString("DepthAndSceneColorPath"); SceneViewNotificationAndLog("Starting to Disable Depth and Scene Color Effects at: " + autoSetupPath); string[] filePaths = System.IO.Directory.GetFiles(autoSetupPath); if(filePaths != null && filePaths.Length > 0) { for(int i = 0; i < filePaths.Length; i++) { Object obj = UnityEditor.AssetDatabase.LoadAssetAtPath(filePaths[i], typeof(Material)); if(obj is Material mat) { string shaderName = mat.shader.name; if(shaderName.Contains("AllIn1Vfx/")) { shaderName = shaderName.Replace("AllIn1Vfx/", ""); if(shaderName.Contains("Vfx")) //Means it is a variation of the asset main shader { if(mat.IsKeywordEnabled("SOFTPART_ON")) mat.DisableKeyword("SOFTPART_ON"); if(mat.IsKeywordEnabled("DEPTHGLOW_ON")) mat.DisableKeyword("DEPTHGLOW_ON"); if(mat.IsKeywordEnabled("SCREENDISTORTION_ON")) mat.DisableKeyword("SCREENDISTORTION_ON"); } } } } } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); SceneViewNotificationAndLog("Disable Depth and Scene Color Effects finished"); } } private static void SceneNotificationsToggle() { float previousLabelWidth = EditorGUIUtility.labelWidth; EditorGUIUtility.labelWidth = 200f; bool areNotificationsEnabled = EditorPrefs.GetInt("DisplaySceneViewNotifications", 1) == 1; areNotificationsEnabled = EditorGUILayout.Toggle("Display Scene View Notifications", areNotificationsEnabled); EditorPrefs.SetInt("DisplaySceneViewNotifications", areNotificationsEnabled ? 1 : 0); EditorGUIUtility.labelWidth = previousLabelWidth; } private static void RefreshLitShader() { GUILayout.Label("Force the Lit Shader to be reconfigured"); GUILayout.Label("If you are getting some error or have changed the render pipeline press the button below"); if(GUILayout.Button("Refresh Lit Shader", GUILayout.MaxWidth(BUTTON_WIDTH))) { AllIn1VfxShaderImporter.ForceReimport(); } } public static bool SetShaderBasedOnEffectsAndPipeline(Material targetMat) { string targetShader = GetTargetShaderName(targetMat); if(!targetMat.shader.name.Equals(targetShader)) { int renderingQueue = targetMat.renderQueue; float zWriteValue = targetMat.GetFloat("_ZWrite"); Shader shader = FindShader(targetShader); if(shader == null) return false; targetMat.shader = shader; targetMat.renderQueue = renderingQueue; targetMat.SetFloat("_ZWrite", zWriteValue); EditorUtility.SetDirty(targetMat); } return true; } private static string GetTargetShaderName(Material targetMat) { string[] oldKeyWords = targetMat.shaderKeywords; string targetShader = "AllIn1Vfx"; string pipeline = "Built-In"; RenderPipelineAsset renderPipelineAsset = GraphicsSettings.defaultRenderPipeline; if(renderPipelineAsset != null) { switch(renderPipelineAsset.GetType().Name) { case"UniversalRenderPipelineAsset": pipeline = "URP"; break; case"HDRenderPipelineAsset": pipeline = "HDRP"; break; } } if(pipeline.Equals("Built-In")) { if(oldKeyWords.Contains("SCREENDISTORTION_ON")) targetShader = "AllIn1VfxGrabPass"; else if(oldKeyWords.Contains("FOG_ON") || oldKeyWords.Contains("SHAPE1SCREENUV_ON") || oldKeyWords.Contains("SHAPE2SCREENUV_ON") || oldKeyWords.Contains("SHAPE3SCREENUV_ON") || oldKeyWords.Contains("SOFTPART_ON") || oldKeyWords.Contains("DEPTHGLOW_ON")) targetShader = "AllIn1VfxBuiltIn"; } else if(pipeline.Equals("URP")) { targetShader = "AllIn1VfxURP"; } else if(pipeline.Equals("HDRP")) { targetShader = "AllIn1VfxHDRP"; } return targetShader; } private static bool IsAssetAFolder(Object obj) { string path = ""; if(obj == null) return false; path = AssetDatabase.GetAssetPath(obj.GetInstanceID()); if(path.Length > 0) { if(Directory.Exists(path)) return true; else return false; } return false; } private void DrawLine(Color color, int thickness = 2, int padding = 10) { Rect r = EditorGUILayout.GetControlRect(GUILayout.Height(padding + thickness)); r.height = thickness; r.y += (padding / 2f); r.x -= 2; r.width += 6; EditorGUI.DrawRect(r, color); } private void OnFocus() { currTab = PlayerPrefs.GetInt("AllIn1VfxWindowTab"); } private void OnLostFocus() { PlayerPrefs.SetInt("AllIn1VfxWindowTab", currTab); } private Texture2D ScaleTexture(Texture2D source, int targetWidth, int targetHeight) { targetWidth = Mathf.ClosestPowerOfTwo(targetWidth); targetHeight = Mathf.ClosestPowerOfTwo(targetHeight); Texture2D result = new Texture2D(targetWidth, targetHeight, source.format, true); Color[] scaledPixels = result.GetPixels(0); float incX = ((float) 1 / source.width) * ((float) source.width / targetWidth); float incY = ((float) 1 / source.height) * ((float) source.height / targetHeight); for(int px = 0; px < scaledPixels.Length; px++) scaledPixels[px] = source.GetPixelBilinear(incX * ((float) px % targetWidth), incY * (float) Mathf.Floor(px / targetWidth)); result.SetPixels(scaledPixels, 0); result.Apply(); return result; } private static Texture2D CreateNormalMap(Texture2D t, float normalMult = 5f, int normalSmooth = 0) { int width = t.width; int height = t.height; Color[] sourcePixels = t.GetPixels(); Color[] resultPixels = new Color[width * height]; Vector3 vScale = new Vector3(0.3333f, 0.3333f, 0.3333f); for(int y = 0; y < height; y++) { for(int x = 0; x < width; x++) { int index = x + y * width; Vector3 cSampleNegXNegY = GetPixelClamped(sourcePixels, x - 1, y - 1, width, height); Vector3 cSampleZerXNegY = GetPixelClamped(sourcePixels, x, y - 1, width, height); Vector3 cSamplePosXNegY = GetPixelClamped(sourcePixels, x + 1, y - 1, width, height); Vector3 cSampleNegXZerY = GetPixelClamped(sourcePixels, x - 1, y, width, height); Vector3 cSamplePosXZerY = GetPixelClamped(sourcePixels, x + 1, y, width, height); Vector3 cSampleNegXPosY = GetPixelClamped(sourcePixels, x - 1, y + 1, width, height); Vector3 cSampleZerXPosY = GetPixelClamped(sourcePixels, x, y + 1, width, height); Vector3 cSamplePosXPosY = GetPixelClamped(sourcePixels, x + 1, y + 1, width, height); float fSampleNegXNegY = Vector3.Dot(cSampleNegXNegY, vScale); float fSampleZerXNegY = Vector3.Dot(cSampleZerXNegY, vScale); float fSamplePosXNegY = Vector3.Dot(cSamplePosXNegY, vScale); float fSampleNegXZerY = Vector3.Dot(cSampleNegXZerY, vScale); float fSamplePosXZerY = Vector3.Dot(cSamplePosXZerY, vScale); float fSampleNegXPosY = Vector3.Dot(cSampleNegXPosY, vScale); float fSampleZerXPosY = Vector3.Dot(cSampleZerXPosY, vScale); float fSamplePosXPosY = Vector3.Dot(cSamplePosXPosY, vScale); float edgeX = (fSampleNegXNegY - fSamplePosXNegY) * 0.25f + (fSampleNegXZerY - fSamplePosXZerY) * 0.5f + (fSampleNegXPosY - fSamplePosXPosY) * 0.25f; float edgeY = (fSampleNegXNegY - fSampleNegXPosY) * 0.25f + (fSampleZerXNegY - fSampleZerXPosY) * 0.5f + (fSamplePosXNegY - fSamplePosXPosY) * 0.25f; Vector2 vEdge = new Vector2(edgeX, edgeY) * normalMult; Vector3 norm = new Vector3(vEdge.x, vEdge.y, 1.0f).normalized; resultPixels[index] = new Color(norm.x * 0.5f + 0.5f, norm.y * 0.5f + 0.5f, norm.z * 0.5f + 0.5f, 1); } } if(normalSmooth > 0) { resultPixels = SmoothNormals(resultPixels, width, height, normalSmooth); } Texture2D texNormal = new Texture2D(width, height, TextureFormat.RGB24, false, false); texNormal.SetPixels(resultPixels); texNormal.Apply(); return texNormal; } private static Vector3 GetPixelClamped(Color[] pixels, int x, int y, int width, int height) { x = Mathf.Clamp(x, 0, width - 1); y = Mathf.Clamp(y, 0, height - 1); Color c = pixels[x + y * width]; return new Vector3(c.r, c.g, c.b); } private static Color[] SmoothNormals(Color[] pixels, int width, int height, int normalSmooth) { Color[] smoothedPixels = new Color[pixels.Length]; float step = 0.00390625f * normalSmooth; for(int y = 0; y < height; y++) { for(int x = 0; x < width; x++) { float pixelsToAverage = 0.0f; Color c = pixels[x + y * width]; pixelsToAverage++; for(int offsetY = -normalSmooth; offsetY <= normalSmooth; offsetY++) { for(int offsetX = -normalSmooth; offsetX <= normalSmooth; offsetX++) { if(offsetX == 0 && offsetY == 0) continue; int sampleX = Mathf.Clamp(x + offsetX, 0, width - 1); int sampleY = Mathf.Clamp(y + offsetY, 0, height - 1); c += pixels[sampleX + sampleY * width]; pixelsToAverage++; } } smoothedPixels[x + y * width] = c / pixelsToAverage; } } return smoothedPixels; } private Color editorColorTint = Color.white; private float brightness = 0f, contrast = 1f, gamma = 1f, exposure = 0f, saturation = 1f, hue = 0f; private bool invert = false, greyscale = false, fullWhite = false, blackBackground = false, alphaGreyscale = false, showOriginalImage = false; private bool isFlipHorizontal = false, isFlipVertical = false; private int rotationAmount = 0; private float exportScale = 1f; private void TextureEditor() { EditorGUI.BeginChangeCheck(); editorTexInput = EditorGUILayout.ObjectField("Image to Edit", editorTexInput, typeof(Texture2D), false, GUILayout.Width(300), GUILayout.Height(50)) as Texture2D; if(EditorGUI.EndChangeCheck()) { if(editorTexInput != null) { SetTextureReadWrite(AssetDatabase.GetAssetPath(editorTexInput), true); editorTex = new Texture2D(editorTexInput.width, editorTexInput.height); editorTex.SetPixels(editorTexInput.GetPixels()); editorTex.Apply(); float aspectRatio = (float) editorTex.width / (float) editorTex.height; int width = Mathf.Min(editorTex.width, 256); editorTex = ScaleTexture(editorTex, width, (int) (width / aspectRatio)); cleanEditorTex = new Texture2D(editorTex.width, editorTex.height); cleanEditorTex.SetPixels(editorTex.GetPixels()); cleanEditorTex.Apply(); SetTextureEditorDefaultValues(); RecalculateEditorTexture(); } else editorTex = null; } DrawLine(Color.grey, 1, 3); if(editorTex != null) { EditorGUILayout.BeginHorizontal(); { if(!showOriginalImage) GUILayout.Label(editorTex); else GUILayout.Label(cleanEditorTex); EditorGUILayout.BeginVertical(); { EditorGUI.BeginChangeCheck(); TextureEditorColorParameter("Color Tint", ref editorColorTint, Color.white); TextureEditorFloatParameter("Brightness", ref brightness, -1f, 5f); TextureEditorFloatParameter("Contrast", ref contrast, 0.0f, 5.0f, 1f); TextureEditorFloatParameter("Gamma", ref gamma, 0.0f, 10f, 1f); TextureEditorFloatParameter("Exposure", ref exposure, -5f, 5f, 0f); TextureEditorFloatParameter("Saturation", ref saturation, 0f, 5f, 1f); TextureEditorFloatParameter("Hue", ref hue, 0f, 360f, 0f); EditorGUILayout.BeginHorizontal(); { invert = EditorGUILayout.Toggle("Invert", invert, GUILayout.Width(253)); greyscale = EditorGUILayout.Toggle("Greyscale", greyscale); } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); { fullWhite = EditorGUILayout.Toggle("Fully white", fullWhite, GUILayout.Width(253)); blackBackground = EditorGUILayout.Toggle("Black background", blackBackground); } EditorGUILayout.EndHorizontal(); alphaGreyscale = EditorGUILayout.Toggle("Greyscale is alpha", alphaGreyscale); if(EditorGUI.EndChangeCheck()) RecalculateEditorTexture(); EditorGUILayout.Space(); EditorGUILayout.BeginHorizontal(); { if (GUILayout.Button("Rotate Left 90°", GUILayout.MaxWidth(210))) RotateEditorTextureLeft(); if(GUILayout.Button("Rotate Right 90°", GUILayout.MaxWidth(210))) for (int i = 0; i < 3; i++) RotateEditorTextureLeft(); } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); { if(GUILayout.Button("Flip Horizontal", GUILayout.MaxWidth(210))) FlipEditorTexture(true); if(GUILayout.Button("Flip Vertical", GUILayout.MaxWidth(210))) FlipEditorTexture(false); } EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); if(!showOriginalImage) { if(GUILayout.Button("Press to show Original Image", GUILayout.MaxWidth(425))) showOriginalImage = true; } else { Color backgroundColor = GUI.backgroundColor; GUI.backgroundColor = Color.red; if(GUILayout.Button("Press to show Editor Image", GUILayout.MaxWidth(425))) showOriginalImage = false; GUI.backgroundColor = backgroundColor; } } EditorGUILayout.EndVertical(); } EditorGUILayout.EndHorizontal(); GUILayout.Label("*Preview is locked to 256px maximum (bigger textures are scaled down), but the image will be saved to its full resolution", EditorStyles.boldLabel); } else { GUILayout.Label("Please select an Image to Edit above", EditorStyles.boldLabel); return; } DrawLine(Color.grey, 1, 3); EditorGUILayout.Space(); TextureEditorFloatParameter("Export Scale", ref exportScale, 0.01f, 2f, 1f); int currWidth = Mathf.ClosestPowerOfTwo((int)(editorTexInput.width * exportScale)); int currHeight = Mathf.ClosestPowerOfTwo((int)(editorTexInput.height * exportScale)); GUILayout.Label("Current export size is: "+ currWidth + " x " + currHeight + " (size snaps to the closest power of 2)", EditorStyles.boldLabel); if(GUILayout.Button("Save Resulting Image as PNG file", GUILayout.MaxWidth(BUTTON_WIDTH))) { string fullPath = AssetDatabase.GetAssetPath(editorTexInput); string path = fullPath.Replace(Path.GetFileName(fullPath), ""); if(File.Exists(fullPath)) fullPath = GetNewValidPath(path + Path.GetFileName(fullPath)); string fileName = fullPath.Replace(path, ""); fileName = fileName.Replace(".png", ""); fullPath = EditorUtility.SaveFilePanel("Save Render Image", path, fileName, "png"); if(fullPath.Length == 0) return; string pingPath = fullPath; ComputeFinalTexture(); byte[] bytes = editorTexInput.EncodeToPNG(); File.WriteAllBytes(pingPath, bytes); AssetDatabase.ImportAsset(pingPath); AssetDatabase.Refresh(); EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath(pingPath, typeof(Texture))); SceneViewNotificationAndLog("Edited Image saved to: " + fullPath); editorTexInput = null; editorTex = null; cleanEditorTex = null; SetTextureEditorDefaultValues(); } } private void ComputeFinalTexture() { Color[] pixels; int texWidth, texHeight; for(int i = 0; i < rotationAmount; i++) { texWidth = editorTexInput.width; texHeight = editorTexInput.height; pixels = editorTexInput.GetPixels(); pixels = RotateClockWise(pixels, texWidth, texHeight); editorTexInput = new Texture2D(texHeight, texWidth); editorTexInput.SetPixels(pixels); editorTexInput.Apply(); } pixels = editorTexInput.GetPixels(); texWidth = editorTexInput.width; texHeight = editorTexInput.height; if(isFlipHorizontal) pixels = FlipHorizontal(pixels, texWidth, texHeight); if(isFlipVertical) pixels = FlipVertical(pixels, texWidth, texHeight); ComputeImageColorFilters(pixels); editorTexInput = new Texture2D(texWidth, texHeight); editorTexInput.SetPixels(pixels); editorTexInput.Apply(); if(Math.Abs(exportScale - 1f) > 0.05f) editorTexInput = ScaleTexture(editorTexInput, (int)(texWidth * exportScale), (int)(texHeight * exportScale)); } private void SetTextureEditorDefaultValues() { editorColorTint = Color.white; brightness = 0f; contrast = 1f; gamma = 1f; exposure = 0f; saturation = 1f; hue = 0f; invert = false; greyscale = false; fullWhite = false; blackBackground = false; alphaGreyscale = false; showOriginalImage = false; isFlipHorizontal = false; isFlipVertical = false; rotationAmount = 0; exportScale = 1f; } private string GetNewValidPath(string path, int i = 1) { int number = i; path = path.Replace(".png", ""); string newPath = path + "_" + number.ToString(); string fullPath = newPath + ".png"; if(System.IO.File.Exists(fullPath)) { number++; fullPath = GetNewValidPath(path, number); } return fullPath; } private void RecalculateEditorTexture() { Color[] pixels = cleanEditorTex.GetPixels(); int texWidth = cleanEditorTex.width; int texHeight = cleanEditorTex.height; ComputeImageColorFilters(pixels); editorTex = new Texture2D(texWidth, texHeight); editorTex.SetPixels(pixels); editorTex.Apply(); } private void ComputeImageColorFilters(Color[] pixels) { float cosHsv = saturation * Mathf.Cos(hue * 3.14159265f / 180f); float sinHsv = saturation * Mathf.Sin(hue * 3.14159265f / 180f); for(int i = 0; i < pixels.Length; i++) { pixels[i].r = Mathf.Clamp01(((pixels[i].r - 0.5f) * contrast) + 0.5f); pixels[i].g = Mathf.Clamp01(((pixels[i].g - 0.5f) * contrast) + 0.5f); pixels[i].b = Mathf.Clamp01(((pixels[i].b - 0.5f) * contrast) + 0.5f); pixels[i] = new Color(Mathf.Clamp01(pixels[i].r * (1 + brightness)), Mathf.Clamp01(pixels[i].g * (1 + brightness)), Mathf.Clamp01(pixels[i].b * (1 + brightness)), pixels[i].a); pixels[i].r = Mathf.Pow(Mathf.Abs(pixels[i].r), gamma); pixels[i].g = Mathf.Pow(Mathf.Abs(pixels[i].g), gamma); pixels[i].b = Mathf.Pow(Mathf.Abs(pixels[i].b), gamma); pixels[i].r = Mathf.Clamp01(pixels[i].r * Mathf.Pow(2, exposure)); pixels[i].g = Mathf.Clamp01(pixels[i].g * Mathf.Pow(2, exposure)); pixels[i].b = Mathf.Clamp01(pixels[i].b * Mathf.Pow(2, exposure)); pixels[i] *= editorColorTint; Color hueShiftColor = pixels[i]; hueShiftColor.r = Mathf.Clamp01((.299f + .701f * cosHsv + .168f * sinHsv) * pixels[i].r + (.587f - .587f * cosHsv + .330f * sinHsv) * pixels[i].g + (.114f - .114f * cosHsv - .497f * sinHsv) * pixels[i].b); hueShiftColor.g = Mathf.Clamp01((.299f - .299f * cosHsv - .328f * sinHsv) * pixels[i].r + (.587f + .413f * cosHsv + .035f * sinHsv) * pixels[i].g + (.114f - .114f * cosHsv + .292f * sinHsv) * pixels[i].b); hueShiftColor.b = Mathf.Clamp01((.299f - .3f * cosHsv + 1.25f * sinHsv) * pixels[i].r + (.587f - .588f * cosHsv - 1.05f * sinHsv) * pixels[i].g + (.114f + .886f * cosHsv - .203f * sinHsv) * pixels[i].b); pixels[i] = hueShiftColor; if(invert) pixels[i] = new Color(1 - pixels[i].r, 1 - pixels[i].g, 1 - pixels[i].b, pixels[i].a); if(greyscale || fullWhite || alphaGreyscale) { float greyScale = pixels[i].r * 0.59f + pixels[i].g * 0.3f + pixels[i].b * 0.11f; if(fullWhite) pixels[i] = new Color(1, 1, 1, greyScale); else if(greyscale) pixels[i] = new Color(greyScale, greyScale, greyScale, pixels[i].a); if(alphaGreyscale) pixels[i] = new Color(pixels[i].r, pixels[i].g, pixels[i].b, greyScale); } if(blackBackground) { if(pixels[i].a < 0.05f) pixels[i] = new Color(pixels[i].a, pixels[i].a, pixels[i].a, 1); else pixels[i] = new Color(pixels[i].r, pixels[i].g, pixels[i].b, 1); } } } private void TextureEditorFloatParameter(string parameterName, ref float parameter, float rangeMin = -100f, float rangeMax = 100f, float resetValue = 0f) { EditorGUILayout.BeginHorizontal(); { parameter = EditorGUILayout.Slider(parameterName, parameter, rangeMin, rangeMax, GUILayout.MaxWidth(400)); GUIContent resetButtonLabel = new GUIContent { text = "R", tooltip = "Resets to default value" }; if(GUILayout.Button(resetButtonLabel, GUILayout.Width(20))) parameter = resetValue; } EditorGUILayout.EndHorizontal(); } private void TextureEditorIntParameter(string parameterName, ref int parameter, int rangeMin = -100, int rangeMax = 100, int resetValue = 0) { EditorGUILayout.BeginHorizontal(); { parameter = EditorGUILayout.IntSlider(parameterName, parameter, rangeMin, rangeMax, GUILayout.MaxWidth(400)); GUIContent resetButtonLabel = new GUIContent { text = "R", tooltip = "Resets to default value" }; if(GUILayout.Button(resetButtonLabel, GUILayout.Width(20))) parameter = resetValue; } EditorGUILayout.EndHorizontal(); } private void TextureEditorColorParameter(string parameterName, ref Color parameter, Color resetValue) { EditorGUILayout.BeginHorizontal(); { GUIContent colorLabel = new GUIContent { text = parameterName, tooltip = parameterName }; parameter = EditorGUILayout.ColorField(colorLabel, parameter, true, true, true, GUILayout.MaxWidth(400)); GUIContent resetButtonLabel = new GUIContent { text = "R", tooltip = "Resets to default value" }; if(GUILayout.Button(resetButtonLabel, GUILayout.Width(20))) parameter = resetValue; } EditorGUILayout.EndHorizontal(); } private void RotateEditorTextureLeft() { Color[] pixels = editorTex.GetPixels(); Color[] pixelsClean = cleanEditorTex.GetPixels(); int texWidth = editorTex.width; int texHeight = editorTex.height; pixels = RotateClockWise(pixels, texWidth, texHeight); pixelsClean = RotateClockWise(pixelsClean, texWidth, texHeight); editorTex = new Texture2D(texHeight, texWidth); //Width and Height get swapped to account for rotation editorTex.SetPixels(pixels); editorTex.Apply(); cleanEditorTex = new Texture2D(texHeight, texWidth); //Width and Height get swapped to account for rotation cleanEditorTex.SetPixels(pixelsClean); cleanEditorTex.Apply(); rotationAmount = (rotationAmount + 1) % 4; } private Color[] RotateClockWise(Color[] pixels, int width, int height) { Color[] outputPixels = new Color[pixels.Length]; for(int y = 0; y < height; y++) { for(int x = 0; x < width; x++) { int i1 = GetPixelIndex(x, height - y - 1, width); int i2 = GetPixelIndex(y, x, height); outputPixels[i2] = pixels[i1]; } } return outputPixels; } private void FlipEditorTexture(bool isHorizontal) { Color[] pixels = editorTex.GetPixels(); Color[] pixelsClean = cleanEditorTex.GetPixels(); int texWidth = editorTex.width; int texHeight = editorTex.height; if(isHorizontal) { pixels = FlipHorizontal(pixels, texWidth, texHeight); pixelsClean = FlipHorizontal(pixelsClean, texWidth, texHeight); isFlipHorizontal = !isFlipHorizontal; } else { pixels = FlipVertical(pixels, texWidth, texHeight); pixelsClean = FlipVertical(pixelsClean, texWidth, texHeight); isFlipVertical = !isFlipVertical; } editorTex = new Texture2D(texWidth, texHeight); editorTex.SetPixels(pixels); editorTex.Apply(); cleanEditorTex = new Texture2D(texWidth, texHeight); cleanEditorTex.SetPixels(pixelsClean); cleanEditorTex.Apply(); } private Color[] FlipHorizontal(Color[] pixels, int width, int height) { Color[] outputPixels = new Color[pixels.Length]; for(int y = 0; y < height; y++) { for(int x = 0; x < width; x++) { int i1 = GetPixelIndex(x, y, width); int i2 = GetPixelIndex(width - 1 - x, y, width); outputPixels[i1] = pixels[i2]; } } return outputPixels; } private Color[] FlipVertical(Color[] pixels, int width, int height) { Color[] outputPixels = new Color[pixels.Length]; for(int y = 0; y < height; y++) { for(int x = 0; x < width; x++) { int i1 = GetPixelIndex(x, y, width); int i2 = GetPixelIndex(x, height - 1 - y, width); outputPixels[i1] = pixels[i2]; } } return outputPixels; } private int GetPixelIndex(int x, int y, int width) { return y * width + x; } private void OnEnable() => GetBasePath(); private static void GetBasePath() { string[] guids = AssetDatabase.FindAssets("t:folder AllIn1VfxToolkit"); if(guids.Length > 0) { basePath = AssetDatabase.GUIDToAssetPath(guids[0]); } else { Debug.LogError("AllIn1VfxToolkit folder not found in the project."); basePath = "Assets/Plugins/AllIn1VfxToolkit"; } } public static string GetMaterialSavePath() { if(!PlayerPrefs.HasKey("All1VfxMaterials")) { GetBasePath(); return basePath + materialsSavesPath; } return PlayerPrefs.GetString("All1VfxMaterials"); } public static string GetRenderImageSavePath() { if(!PlayerPrefs.HasKey("All1VfxRenderImages")) { GetBasePath(); return basePath + renderImagesSavesPath; } return PlayerPrefs.GetString("All1VfxRenderImages"); } public static string GetParticlePresetsPath() { if(!PlayerPrefs.HasKey("All1VfxParticlePresets")) { GetBasePath(); return basePath + particlePresetsSavesPath; } return PlayerPrefs.GetString("All1VfxParticlePresets"); } public static void SceneViewNotificationAndLog(string message) { Debug.Log(message); ShowSceneViewNotification(message); } public static Shader FindShader(string shaderName) { string[] guids = AssetDatabase.FindAssets($"{shaderName} t:shader"); foreach(string guid in guids) { string path = AssetDatabase.GUIDToAssetPath(guid); Shader shader = AssetDatabase.LoadAssetAtPath(path); if(shader != null) { string fullShaderName = shader.name; string actualShaderName = fullShaderName.Substring(fullShaderName.LastIndexOf('/') + 1); if(actualShaderName == shaderName) return shader; } } return null; } public static void ShowSceneViewNotification(string message) { bool showNotification = EditorPrefs.GetInt("DisplaySceneViewNotifications", 1) == 1; if(!showNotification) return; GUIContent content = new GUIContent(message); #if UNITY_2019_1_OR_NEWER SceneView.lastActiveSceneView.ShowNotification(content, 1.5f); #else SceneView.lastActiveSceneView.ShowNotification(content); #endif } } } #endif