summaryrefslogtreecommitdiff
path: root/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor
diff options
context:
space:
mode:
authortylermurphy534 <tylermurphy534@gmail.com>2022-11-06 15:12:42 -0500
committertylermurphy534 <tylermurphy534@gmail.com>2022-11-06 15:12:42 -0500
commiteb84bb298d2b95aec7b2ae12cbf25ac64f25379a (patch)
treeefd616a157df06ab661c6d56651853431ac6b08b /VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor
downloadunityprojects-eb84bb298d2b95aec7b2ae12cbf25ac64f25379a.tar.gz
unityprojects-eb84bb298d2b95aec7b2ae12cbf25ac64f25379a.tar.bz2
unityprojects-eb84bb298d2b95aec7b2ae12cbf25ac64f25379a.zip
move to self host
Diffstat (limited to 'VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor')
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/BakeToVertexColorsEditor.cs360
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/BakeToVertexColorsEditor.cs.meta11
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/GradientFlood.cs410
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/GradientFlood.cs.meta11
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/ModularShadersGeneratorWindow.cs211
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/ModularShadersGeneratorWindow.cs.meta3
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/PoiData.cs70
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/PoiData.cs.meta11
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/PoiHelpers.cs190
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/PoiHelpers.cs.meta11
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/TextureUtility.cs1244
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/TextureUtility.cs.meta11
12 files changed, 2543 insertions, 0 deletions
diff --git a/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/BakeToVertexColorsEditor.cs b/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/BakeToVertexColorsEditor.cs
new file mode 100644
index 00000000..13fc2194
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/BakeToVertexColorsEditor.cs
@@ -0,0 +1,360 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+
+
+namespace Poi
+{
+ public class BakeToVertexColorsEditor : EditorWindow
+ {
+ //Window
+ static readonly Vector2 MIN_WINDOW_SIZE = new Vector2(316, 210);
+
+ // Version
+ Version version = new Version(1, 2);
+ string SubTitle
+ {
+ get
+ {
+ if(string.IsNullOrWhiteSpace(_subTitle))
+ _subTitle = "by Pumkin - v" + version.ToString();
+ return _subTitle;
+ }
+ }
+
+
+ //Strings
+ const string log_prefix = "<color=blue>Poi:</color> "; //color is hex or name
+
+ const string bakedSuffix_normals = "baked_normals";
+ const string bakedSuffix_position = "baked_position";
+
+ const string bakesFolderName = "Baked";
+ const string defaultUnityAssetBakesFolder = "Default Unity Resources";
+
+ const string hint_bakeAverageNormals = "Use this if you want seamless outlines";
+ const string hint_bakeVertexPositions = "Use this if you want scrolling emission";
+
+ const string button_bakeAverageNormals = "Bake Averaged Normals";
+ const string button_bakeVertexPositions = "Bake Vertex Positions";
+
+ const string warning_noMeshesDetected =
+ "No meshes detected in selection. Make sure your object has a Skinned Mesh Renderer or a Mesh Renderer with a valid Mesh assigned";
+
+ //Properties
+ static GameObject Selection
+ {
+ get => _selection;
+ set => _selection = value;
+ }
+
+ [MenuItem("Tools/Poi/Vertex Color Baker", priority = 11)]
+ public static void ShowWindow()
+ {
+ //Show existing window instance. If one doesn't exist, make one.
+ EditorWindow editorWindow = GetWindow(typeof(BakeToVertexColorsEditor));
+ editorWindow.autoRepaintOnSceneChange = true;
+ editorWindow.minSize = MIN_WINDOW_SIZE;
+
+ editorWindow.Show();
+ editorWindow.titleContent = new GUIContent("Bake Colors");
+ }
+
+ void OnGUI()
+ {
+ EditorGUILayout.LabelField("Poi Vertex Color Baker", PoiStyles.TitleLabel);
+ EditorGUILayout.LabelField(SubTitle);
+
+ PoiHelpers.DrawLine();
+
+ EditorGUI.BeginChangeCheck();
+ GameObject obj = EditorGUILayout.ObjectField("Avatar", Selection, typeof(GameObject), true) as GameObject;
+ if(EditorGUI.EndChangeCheck())
+ Selection = obj;
+
+ PoiHelpers.DrawLine();
+
+ EditorGUI.BeginDisabledGroup(!Selection);
+ {
+ EditorGUILayout.HelpBox(hint_bakeAverageNormals, MessageType.Info);
+ if(GUILayout.Button(button_bakeAverageNormals))
+ {
+ var meshes = GetAllMeshInfos(Selection);
+ if(meshes == null || meshes.Length == 0)
+ Debug.LogWarning(log_prefix + warning_noMeshesDetected);
+ else
+ BakeAveragedNormalsToColors(meshes);
+ }
+
+ PoiHelpers.DrawLine(true, false);
+ EditorGUILayout.HelpBox(hint_bakeVertexPositions, MessageType.Info);
+ if(GUILayout.Button(button_bakeVertexPositions))
+ {
+ var meshes = GetAllMeshInfos(Selection);
+ if(meshes == null || meshes.Length == 0)
+ Debug.LogWarning(log_prefix + warning_noMeshesDetected);
+ else
+ BakePositionsToColors(meshes);
+ }
+ }
+ EditorGUI.EndDisabledGroup();
+ }
+
+ /// <summary>
+ /// Saves a mesh in the same folder as the original asset
+ /// </summary>
+ /// <param name="mesh"></param>
+ /// <param name="newName">The new name of the mesh</param>
+ /// <returns>Returns the newly created mesh asset</returns>
+ static Mesh SaveMeshAsset(Mesh mesh, string newName)
+ {
+ string assetPath = AssetDatabase.GetAssetPath(mesh);
+
+ if(string.IsNullOrWhiteSpace(assetPath))
+ {
+ Debug.LogWarning(log_prefix + "Invalid asset path for " + mesh.name);
+ return null;
+ }
+
+ //Figure out folder name
+ string bakesDir = $"{Path.GetDirectoryName(assetPath)}";
+
+ //Handle default assets
+ if(bakesDir.StartsWith("Library"))
+ bakesDir = $"Assets\\{defaultUnityAssetBakesFolder}";
+
+ if(!bakesDir.EndsWith(bakesFolderName))
+ bakesDir += $"\\{bakesFolderName}";
+
+ if(!assetPath.Contains('.'))
+ assetPath += '\\';
+
+ PoiHelpers.EnsurePathExistsInAssets(bakesDir);
+
+ //Generate path
+ string pathNoExt = Path.Combine(bakesDir, newName);
+ string newPath = AssetDatabase.GenerateUniqueAssetPath($"{pathNoExt}.mesh");
+
+ //Save mesh, load it back, assign to renderer
+ Mesh newMesh = Instantiate(mesh);
+ AssetDatabase.CreateAsset(newMesh, newPath);
+
+ newMesh = AssetDatabase.LoadAssetAtPath<Mesh>(newPath);
+
+ if(newMesh == null)
+ {
+ Debug.Log(log_prefix + "Failed to load saved mesh");
+ return null;
+ }
+
+ EditorGUIUtility.PingObject(newMesh);
+ return newMesh;
+ }
+
+ /// <summary>
+ /// Sets the sharedMesh of a Skinned Mesh Renderer or Mesh Filter attached to a Mesh Renderer
+ /// </summary>
+ /// <param name="render"></param>
+ /// <param name="mesh"></param>
+ /// <returns></returns>
+ static bool SetRendererSharedMesh(Renderer render, Mesh mesh)
+ {
+ if(render is SkinnedMeshRenderer smr)
+ smr.sharedMesh = mesh;
+ else if(render is MeshRenderer mr)
+ {
+ var filter = mr.gameObject.GetComponent<MeshFilter>();
+ filter.sharedMesh = mesh;
+ }
+ else
+ return false;
+ return true;
+ }
+
+ static MeshInfo[] GetAllMeshInfos(GameObject obj)
+ {
+ return GetAllMeshInfos(obj?.GetComponentsInChildren<Renderer>(true));
+ }
+
+ static MeshInfo[] GetAllMeshInfos(params Renderer[] renderers)
+ {
+ var infos = renderers?.Select(ren =>
+ {
+ MeshInfo info = new MeshInfo();
+ if(ren is SkinnedMeshRenderer smr)
+ {
+ Mesh bakedMesh = new Mesh();
+ Transform tr = smr.gameObject.transform;
+ Quaternion origRot = tr.localRotation;
+ Vector3 origScale = tr.localScale;
+
+ tr.localRotation = Quaternion.identity;
+ tr.localScale = Vector3.one;
+
+ smr.BakeMesh(bakedMesh);
+
+ tr.localRotation = origRot;
+ tr.localScale = origScale;
+
+ info.sharedMesh = smr.sharedMesh;
+ info.bakedVertices = bakedMesh?.vertices;
+ info.bakedNormals = bakedMesh?.normals;
+ info.ownerRenderer = smr;
+ if(!info.sharedMesh)
+ Debug.LogWarning(log_prefix + $"Skinned Mesh Renderer at <b>{info.ownerRenderer.gameObject.name}</b> doesn't have a valid mesh");
+ }
+ else if(ren is MeshRenderer mr)
+ {
+ info.sharedMesh = mr.GetComponent<MeshFilter>()?.sharedMesh;
+ info.bakedVertices = info.sharedMesh?.vertices;
+ info.bakedNormals = info.sharedMesh?.normals;
+ info.ownerRenderer = mr;
+ if(!info.sharedMesh)
+ Debug.LogWarning(log_prefix + $"Mesh renderer at <b>{info.ownerRenderer.gameObject.name}</b> doesn't have a mesh filter with a valid mesh");
+ }
+ return info;
+ }).ToArray();
+
+ return infos;
+ }
+
+ static void BakePositionsToColors(MeshInfo[] meshInfos)
+ {
+ var queue = new Dictionary<MeshInfo, Mesh>();
+ try
+ {
+ AssetDatabase.StartAssetEditing();
+ foreach(var meshInfo in meshInfos)
+ {
+ if(!meshInfo.sharedMesh)
+ continue;
+
+ Vector3[] verts = meshInfo.bakedVertices; //accessing mesh.vertices on every iteration is very slow
+ Color[] colors = new Color[verts.Length];
+ for(int i = 0; i < verts.Length; i++)
+ colors[i] = new Color(verts[i].x, verts[i].y, verts[i].z);
+ meshInfo.sharedMesh.colors = colors;
+
+ //Create new mesh asset and add it to queue
+ string name = PoiHelpers.AddSuffix(meshInfo.ownerRenderer.gameObject.name, bakedSuffix_position);
+ Mesh newMesh = SaveMeshAsset(meshInfo.sharedMesh, name);
+ if(newMesh)
+ queue.Add(meshInfo, newMesh);
+ }
+ }
+ catch(Exception ex)
+ {
+ Debug.LogException(ex);
+ }
+ finally
+ {
+ AssetDatabase.StopAssetEditing();
+ }
+
+ //After all meshes are imported assign the meshes
+ foreach(var kv in queue)
+ {
+ SetRendererSharedMesh(kv.Key.ownerRenderer, kv.Value);
+ }
+ }
+
+ static void BakeAveragedNormalsToColors(params MeshInfo[] infos)
+ {
+ var queue = new Dictionary<MeshInfo, Mesh>();
+ try
+ {
+ AssetDatabase.StartAssetEditing();
+ foreach(var meshInfo in infos)
+ {
+ if(!meshInfo.sharedMesh)
+ continue;
+
+ Vector3[] verts = meshInfo.bakedVertices;
+ Vector3[] normals = meshInfo.bakedNormals;
+ VertexInfo[] vertInfo = new VertexInfo[verts.Length];
+ for(int i = 0; i < verts.Length; i++)
+ {
+ vertInfo[i] = new VertexInfo()
+ {
+ vertex = verts[i],
+ originalIndex = i,
+ normal = normals[i]
+ };
+ }
+ var groups = vertInfo.GroupBy(x => x.vertex);
+ VertexInfo[] processedVertInfo = new VertexInfo[vertInfo.Length];
+ int index = 0;
+ foreach(IGrouping<Vector3, VertexInfo> group in groups)
+ {
+ Vector3 avgNormal = Vector3.zero;
+ foreach(VertexInfo item in group)
+ avgNormal += item.normal;
+
+ avgNormal /= group.Count();
+ foreach(VertexInfo item in group)
+ {
+ processedVertInfo[index] = new VertexInfo()
+ {
+ vertex = item.vertex,
+ originalIndex = item.originalIndex,
+ normal = item.normal,
+ averagedNormal = avgNormal
+ };
+ index++;
+ }
+ }
+ Color[] colors = new Color[verts.Length];
+ for(int i = 0; i < processedVertInfo.Length; i++)
+ {
+ VertexInfo info = processedVertInfo[i];
+
+ int origIndex = info.originalIndex;
+ Vector3 normal = info.averagedNormal;
+ Color normColor = new Color(normal.x, normal.y, normal.z, 1);
+ colors[origIndex] = normColor;
+ }
+ meshInfo.sharedMesh.colors = colors;
+
+ string name = PoiHelpers.AddSuffix(meshInfo.ownerRenderer.gameObject.name, bakedSuffix_normals);
+ Mesh newMesh = SaveMeshAsset(meshInfo.sharedMesh, name);
+ if(newMesh)
+ queue.Add(meshInfo, newMesh);
+ }
+ }
+ catch(Exception ex)
+ {
+ Debug.LogException(ex);
+ }
+ finally
+ {
+ AssetDatabase.StopAssetEditing();
+ }
+
+ //Assign all new meshes to their renderers
+ foreach(var kv in queue)
+ SetRendererSharedMesh(kv.Key.ownerRenderer, kv.Value);
+ }
+
+ struct MeshInfo
+ {
+ public Renderer ownerRenderer;
+ public Mesh sharedMesh;
+ public Vector3[] bakedVertices;
+ public Vector3[] bakedNormals;
+ }
+
+ struct VertexInfo
+ {
+ public Vector3 vertex;
+ public int originalIndex;
+ public Vector3 normal;
+ public Vector3 averagedNormal;
+ }
+
+ static GameObject _selection;
+ private string _subTitle;
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/BakeToVertexColorsEditor.cs.meta b/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/BakeToVertexColorsEditor.cs.meta
new file mode 100644
index 00000000..d603b15a
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/BakeToVertexColorsEditor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 3f398d68f8c01b54485d2a04a13c958b
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/GradientFlood.cs b/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/GradientFlood.cs
new file mode 100644
index 00000000..108976dc
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/GradientFlood.cs
@@ -0,0 +1,410 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+using Debug = UnityEngine.Debug;
+
+//Made by Dreadrith#3238
+//Discord: https://discord.gg/ZsPfrGn
+//Github: https://github.com/Dreadrith/DreadScripts
+//Gumroad: https://gumroad.com/dreadrith
+//Ko-fi: https://ko-fi.com/dreadrith
+
+namespace DreadScripts
+{
+ public class GradientFlood : EditorWindow
+ {
+
+ #region Automated Variables
+ private static float modifiedBoundRange;
+ private static Texture2D titleTexture;
+ #endregion
+
+ #region Input
+ public static Texture2D pathTexture;
+ public static Color startPixelsColor = Color.red;
+ public static Color limitPixelsColor = Color.white;
+ public static GradientType gradientType = GradientType.DataGradient;
+
+ public static Color tintColor = Color.white;
+ public static Gradient gradientColor = new Gradient();
+
+ public static float gradientDistribution = 1;
+ public static float startColorTolerance = 0.05f;
+ public static float limitColorTolerance = 0.05f;
+ public static float rangeLowerBound;
+ public static float rangeUpperBound = 255;
+ public static bool invertGradient;
+ public static bool loopGradient;
+ public static bool applyGradientAlpha;
+ #endregion
+
+ public event EventHandler TextureGenerated;
+ public class TextureGeneratedEventArgs : EventArgs
+ {
+ public Texture2D generated_texture;
+ public Texture2D original_texture;
+ }
+
+ public enum GradientType
+ {
+ DataGradient,
+ TintedGradient,
+ GradientGradient
+ }
+ [MenuItem("Tools/Poi/Gradient Flood")]
+ private static void showWindow()
+ {
+ EditorWindow w = GetWindow<GradientFlood>(false, "Gradient Flood", true);
+ if (!titleTexture)
+ {
+ titleTexture = GetColors((Texture2D)EditorGUIUtility.IconContent("Texture2D Icon").image, 16, 16, out _);
+ titleTexture.Apply();
+ }
+
+ w.titleContent.image = titleTexture;
+ w.minSize = new Vector2(423, 253);
+ }
+
+ private void OnGUI()
+ {
+ GUIStyle centeredTitle = new GUIStyle("boldlabel") {alignment = TextAnchor.MiddleCenter, fontSize = 16};
+ using (new GUILayout.VerticalScope("helpbox"))
+ {
+ GUILayout.Label("Texture", centeredTitle);
+ using (new GUILayout.HorizontalScope())
+ {
+ GUILayout.FlexibleSpace();
+ EditorGUIUtility.labelWidth = 1;
+ pathTexture = (Texture2D) EditorGUILayout.ObjectField(string.Empty, pathTexture, typeof(Texture2D), false, GUILayout.Width(80), GUILayout.Height(80));
+ EditorGUIUtility.labelWidth = 0;
+
+ GUILayout.FlexibleSpace();
+ }
+
+ using (new GUILayout.HorizontalScope("box"))
+ gradientType = (GradientType)EditorGUILayout.EnumPopup("Gradient Type", gradientType);
+
+
+
+ switch (gradientType)
+ {
+ case GradientType.TintedGradient:
+ using (new GUILayout.HorizontalScope("box"))
+ tintColor = EditorGUILayout.ColorField("Tint", tintColor);
+ break;
+ case GradientType.GradientGradient:
+ using (new GUILayout.HorizontalScope("box"))
+ gradientColor = EditorGUILayout.GradientField("Gradient", gradientColor);
+ break;
+ }
+
+
+ if (gradientType > 0)
+ {
+ using (new GUILayout.HorizontalScope("box"))
+ {
+ GUILayout.Label("Color Range");
+
+ EditorGUI.BeginChangeCheck();
+
+ rangeLowerBound = EditorGUILayout.DelayedIntField((int) rangeLowerBound, GUI.skin.label, GUILayout.Width(28));
+ EditorGUILayout.MinMaxSlider(ref rangeLowerBound, ref rangeUpperBound, 0, 255);
+
+ rangeUpperBound = EditorGUILayout.DelayedIntField((int) rangeUpperBound, GUI.skin.label, GUILayout.Width(28));
+
+ if (EditorGUI.EndChangeCheck())
+ {
+ rangeUpperBound = Mathf.Clamp((int) rangeUpperBound, 0, 255);
+ rangeLowerBound = Mathf.Max(0, Mathf.Min(rangeUpperBound, rangeLowerBound));
+ }
+ }
+ }
+
+
+ using (new GUILayout.HorizontalScope("box"))
+ {
+ using (new GUILayout.HorizontalScope())
+ GUILayout.Label("Tolerance");
+
+ using (new GUILayout.HorizontalScope())
+ {
+ EditorGUIUtility.labelWidth = 40;
+ startColorTolerance = EditorGUILayout.Slider("Start", startColorTolerance, 0, 1);
+ limitColorTolerance = EditorGUILayout.Slider("Limit", limitColorTolerance, 0, 1);
+ EditorGUIUtility.labelWidth = 0;
+ }
+ }
+
+ using (new GUILayout.HorizontalScope("box"))
+ {
+ invertGradient = EditorGUILayout.Toggle("Invert Gradient", invertGradient);
+
+ if (gradientType > 0) applyGradientAlpha = EditorGUILayout.Toggle("Apply Gradient Alpha", applyGradientAlpha);
+ }
+
+ using (new GUILayout.HorizontalScope("box"))
+ {
+ loopGradient = EditorGUILayout.Toggle("Loop Gradient", loopGradient);
+ if (loopGradient) gradientDistribution = EditorGUILayout.FloatField("Distribution", gradientDistribution);
+ }
+
+ using (new EditorGUI.DisabledScope(!pathTexture))
+ if (GUILayout.Button("Fill"))
+ {
+ Texture2D generated = GenerateFilledTexture(pathTexture);
+ TextureGeneratedEventArgs args = new TextureGeneratedEventArgs();
+ args.original_texture = pathTexture;
+ args.generated_texture = generated;
+ TextureGenerated?.Invoke(this, args);
+ }
+
+ }
+ Credit();
+ }
+
+ public static Texture2D GenerateFilledTexture(Texture2D texture)
+ {
+ if (!loopGradient) gradientDistribution = 1;
+ if (gradientType == GradientType.DataGradient)
+ {
+ rangeLowerBound = 1;
+ rangeUpperBound = 255;
+ }
+
+ int width = texture.width;
+ modifiedBoundRange = rangeUpperBound / 255 - rangeLowerBound / 255;
+ Texture2D gradientTexture = GetColors(texture, out Color[] ogColors);
+ GradientFillPixel[] gradientPixels = new GradientFillPixel[ogColors.Length];
+
+ Queue<GradientFillPixel> pixelsToExpand = new Queue<GradientFillPixel>();
+
+ for (int i = 0; i < ogColors.Length; i++)
+ {
+ gradientPixels[i] = new GradientFillPixel(ogColors[i], i);
+ if (!gradientPixels[i].isLimit && gradientPixels[i].isFilled) pixelsToExpand.Enqueue(gradientPixels[i]);
+ }
+
+ if (pixelsToExpand.Count == 0)
+ Debug.LogWarning("<color=red>[GPFiller]</color> No start pixels were found! If start pixels exist, try adjusting the color tolerance.");
+
+ bool IsValidFillIndex(int index, out GradientFillPixel pixelToFill)
+ {
+
+ if (index >= 0 && index < ogColors.Length)
+ {
+ pixelToFill = gradientPixels[index];
+ if (!pixelToFill.isLimit && !pixelToFill.isFilled)
+ {
+ pixelsToExpand.Enqueue(pixelToFill);
+ return true;
+ }
+ }
+
+ pixelToFill = null;
+ return false;
+ }
+
+ void FillIndex(GradientFillPixel fillerPixel, int index)
+ {
+ if (!IsValidFillIndex(index, out GradientFillPixel nextPixel)) return;
+ nextPixel.gradientValue = fillerPixel.gradientValue + gradientDistribution;
+ nextPixel.isFilled = true;
+ }
+
+ while (pixelsToExpand.Any())
+ {
+ var pixel = pixelsToExpand.Dequeue();
+
+ bool isTopMost = pixel.arrayIndex < width;
+ bool isBottomMost = pixel.arrayIndex >= ogColors.Length - width;
+ bool isLeftMost = pixel.arrayIndex % width == 0;
+ bool isRightMost = (pixel.arrayIndex + 1) % width == 0;
+
+ if (!isRightMost)
+ FillIndex(pixel, pixel.arrayIndex + 1);
+
+ if (!isLeftMost)
+ FillIndex(pixel, pixel.arrayIndex - 1);
+
+ if (!isBottomMost)
+ FillIndex(pixel, pixel.arrayIndex + width);
+
+ if (!isTopMost)
+ FillIndex(pixel, pixel.arrayIndex - width);
+
+ if (!isBottomMost && !isRightMost)
+ FillIndex(pixel, pixel.arrayIndex + width + 1);
+
+ if (!isBottomMost && !isLeftMost)
+ FillIndex(pixel, pixel.arrayIndex + width - 1);
+
+ if (!isTopMost && !isRightMost)
+ FillIndex(pixel, pixel.arrayIndex - width + 1);
+
+ if (!isTopMost && !isLeftMost)
+ FillIndex(pixel, pixel.arrayIndex - width - 1);
+ }
+
+
+ float maxGradientValue = gradientPixels.Max(p => p.gradientValue);
+ if (gradientType == GradientType.DataGradient) maxGradientValue /= 4;
+
+ for (int i = 0; i < ogColors.Length; i++)
+ {
+ var f = gradientPixels[i].GetFloatValue(maxGradientValue);
+
+ if (f == 0) ogColors[i] = Color.clear;
+ else
+ {
+
+ if (gradientType == GradientType.TintedGradient)
+ ogColors[i] = new Color(f * tintColor.r, f * tintColor.g, f * tintColor.b, gradientPixels[i].isLimit ? 1 : (applyGradientAlpha ? f : 1) * tintColor.a);
+ else if (gradientType == GradientType.GradientGradient)
+ {
+ Color gradColor = gradientColor.Evaluate(f);
+ gradColor.a = gradientPixels[i].isLimit ? 1 : (applyGradientAlpha ? f : 1) * gradColor.a;
+ ogColors[i] = gradColor;
+ }
+ else
+ {
+ if (f <= 1) ogColors[i] = new Color(f % 1, 0, 0, 0);
+
+ else if (f <= 2) ogColors[i] = new Color(0, f%1, 0, 0);
+
+ else if (f <= 3) ogColors[i] = new Color(0, 0, f % 1, 0);
+
+ else ogColors[i] = new Color(0, 0, 0, f % 1);
+
+ }
+ }
+ }
+
+ gradientTexture.SetPixels(ogColors);
+ gradientTexture.Apply();
+
+ string assetPath = AssetDatabase.GetAssetPath(pathTexture);
+ string ext = Path.GetExtension(assetPath);
+ byte[] data;
+ switch (ext)
+ {
+ case ".jpeg" when !applyGradientAlpha && gradientType != GradientType.DataGradient:
+ case ".jpg" when !applyGradientAlpha && gradientType != GradientType.DataGradient:
+ ext = ".jpg";
+ data = gradientTexture.EncodeToJPG(100);
+ break;
+ case ".tga":
+ data = gradientTexture.EncodeToTGA();
+ break;
+ default:
+ ext = ".png";
+ data = gradientTexture.EncodeToPNG();
+ break;
+ }
+
+
+ DestroyImmediate(gradientTexture);
+
+ string savePath = AssetDatabase.GenerateUniqueAssetPath($"{Path.GetDirectoryName(assetPath)}/{pathTexture.name}{ext}");
+ SaveTexture(data, savePath);
+ CopyTextureSettings(assetPath, savePath);
+ return AssetDatabase.LoadAssetAtPath<Texture2D>(savePath);
+ }
+
+ public static Texture2D GetColors(Texture2D texture, int width, int height, out Color[] Colors, bool unloadTempTexture = false)
+ {
+ //Thanks to
+ //https://gamedev.stackexchange.com/questions/92285/unity3d-resize-texture-without-corruption
+ texture.filterMode = FilterMode.Point;
+ RenderTexture rt = RenderTexture.GetTemporary(width, height);
+
+ rt.filterMode = FilterMode.Point;
+ RenderTexture.active = rt;
+ Graphics.Blit(texture, rt);
+ Texture2D newTexture = new Texture2D(width, height);
+ newTexture.ReadPixels(new Rect(0, 0, width, height), 0, 0);
+ Color[] myColors = newTexture.GetPixels();
+ RenderTexture.active = null;
+ /////////////////////
+ Colors = myColors;
+ if (unloadTempTexture)
+ {
+ DestroyImmediate(newTexture);
+ return null;
+ }
+ return newTexture;
+ }
+
+ public static Texture2D GetColors(Texture2D texture, out Color[] Colors, bool unloadTempTexture = false)
+ {
+ return GetColors(texture, texture.width, texture.height, out Colors, unloadTempTexture);
+ }
+
+ private static void CopyTextureSettings(string from, string to)
+ {
+ TextureImporter source = (TextureImporter)AssetImporter.GetAtPath(from);
+ TextureImporterSettings sourceSettings = new TextureImporterSettings();
+ source.ReadTextureSettings(sourceSettings);
+
+ TextureImporter destination = (TextureImporter)AssetImporter.GetAtPath(to);
+ destination.SetTextureSettings(sourceSettings);
+ destination.maxTextureSize = source.maxTextureSize;
+ destination.textureCompression = source.textureCompression;
+ destination.crunchedCompression = source.crunchedCompression;
+ destination.SaveAndReimport();
+ }
+
+ private static void SaveTexture(byte[] textureEncoding, string path)
+ {
+ using (System.IO.FileStream stream = System.IO.File.Create(path))
+ stream.Write(textureEncoding, 0, textureEncoding.Length);
+ AssetDatabase.Refresh();
+ EditorGUIUtility.PingObject(AssetDatabase.LoadMainAssetAtPath(path));
+ }
+
+ public class GradientFillPixel
+ {
+ public readonly bool isLimit;
+ public bool isFilled;
+ public float gradientValue;
+ public int arrayIndex;
+
+ public GradientFillPixel(Color pixelColor, int index)
+ {
+ isLimit = Mathf.Abs(pixelColor.r - limitPixelsColor.r) < limitColorTolerance &&
+ Mathf.Abs(pixelColor.g - limitPixelsColor.g) < limitColorTolerance &&
+ Mathf.Abs(pixelColor.b - limitPixelsColor.b) < limitColorTolerance;
+
+ isFilled = Mathf.Abs(pixelColor.r - startPixelsColor.r) < startColorTolerance &&
+ Mathf.Abs(pixelColor.g - startPixelsColor.g) < startColorTolerance &&
+ Mathf.Abs(pixelColor.b - startPixelsColor.b) < startColorTolerance;
+
+ gradientValue = (int)rangeLowerBound;
+ arrayIndex = index;
+ }
+
+ public float GetFloatValue(float maxGradientValue)
+ {
+ if (isLimit || !isFilled) return 0;
+ float floatValue = !loopGradient ? gradientValue / maxGradientValue : (gradientValue % 255 * (gradientType == GradientType.DataGradient ? 4 : 1)) / 255f;
+ float adjustedValue = floatValue * modifiedBoundRange + rangeLowerBound / 255;
+ return invertGradient ? (gradientType == GradientType.DataGradient ? 4 : 1) - adjustedValue : adjustedValue;
+ }
+ }
+
+ private static void Credit()
+ {
+ using (new GUILayout.HorizontalScope())
+ {
+ GUILayout.FlexibleSpace();
+ if (GUILayout.Button("Made By Dreadrith#3238", "boldlabel"))
+ Application.OpenURL("https://linktr.ee/Dreadrith");
+ }
+ }
+ }
+
+
+}
diff --git a/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/GradientFlood.cs.meta b/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/GradientFlood.cs.meta
new file mode 100644
index 00000000..3da8dcc5
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/GradientFlood.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 88da795f54b1f2242a30125dc4188486
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/ModularShadersGeneratorWindow.cs b/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/ModularShadersGeneratorWindow.cs
new file mode 100644
index 00000000..8ead5f31
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/ModularShadersGeneratorWindow.cs
@@ -0,0 +1,211 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Poiyomi.ModularShaderSystem;
+using UnityEditor;
+using UnityEngine;
+using UnityEngine.UI;
+using UnityEngine.UIElements;
+using Button = UnityEngine.UIElements.Button;
+using Toggle = UnityEngine.UIElements.Toggle;
+
+namespace PoiyomiPatreon.Scripts.poi_tools.Editor
+{
+ public class ModularShadersGeneratorElement : VisualElement
+ {
+ private bool _isSelected;
+
+ public bool IsSelected
+ {
+ get => _isSelected;
+ set
+ {
+ if (_isErrored) return;
+ _isSelected = value;
+ _toggle.SetValueWithoutNotify(_isSelected);
+ }
+ }
+
+ public ModularShader Shader { get; set; }
+
+ private readonly Toggle _toggle;
+ private readonly bool _isErrored;
+ public ModularShadersGeneratorElement(ModularShader shader)
+ {
+ Shader = shader;
+ style.flexDirection = FlexDirection.Row;
+ _toggle = new Toggle();
+ _toggle.RegisterValueChangedCallback(evt => IsSelected = evt.newValue);
+ Add(_toggle);
+ Add(new Label(Shader.Name));
+ var issues = ShaderGenerator.CheckShaderIssues(shader);
+ if (issues.Count > 0)
+ {
+ _isErrored = true;
+ _toggle.SetEnabled(false);
+ VisualElement element = new VisualElement();
+ element.AddToClassList("error");
+ element.tooltip = "Modular shader has the following errors: \n -" + string.Join("\n -", issues);
+ Add(element);
+ }
+ }
+ }
+
+ public class ModularShadersGeneratorWindow : EditorWindow
+ {
+ [MenuItem("Poi/Tools/Modular Shaders Generator")]
+ private static void ShowWindow()
+ {
+ var window = GetWindow<ModularShadersGeneratorWindow>();
+ window.titleContent = new GUIContent("Modular Shaders Generator");
+ window.Show();
+ }
+
+ private VisualElement _root;
+ private List<ModularShadersGeneratorElement> _elements;
+ private string _folderPath = "Assets/_poiyomiShaders/ModularShader";
+
+ private void CreateGUI()
+ {
+ _root = rootVisualElement;
+ try
+ {
+ Reload();
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e);
+ }
+ }
+
+ private void Reload()
+ {
+ _root.Clear();
+
+ var styleSheet = Resources.Load<StyleSheet>("Poi/ModularShadersGeneratorStyle");
+ _root.styleSheets.Add(styleSheet);
+
+ var view = new ScrollView(ScrollViewMode.Vertical);
+
+ var selectButtons = new VisualElement();
+ selectButtons.AddToClassList("buttons-area");
+
+ var selectAll = new Button();
+ selectAll.text = "Select all";
+ selectAll.style.flexGrow = 1;
+ selectAll.clicked += () =>
+ {
+ foreach (var element in _elements)
+ element.IsSelected = true;
+ };
+ selectButtons.Add(selectAll);
+
+ var deselectAll = new Button();
+ deselectAll.text = "Deselect all";
+ deselectAll.style.flexGrow = 1;
+ deselectAll.clicked += () =>
+ {
+ foreach (var element in _elements)
+ element.IsSelected = false;
+ };
+ selectButtons.Add(deselectAll);
+
+ var toggleSelections = new Button();
+ toggleSelections.text = "Toggle selections";
+ toggleSelections.style.flexGrow = 1;
+ toggleSelections.clicked += () =>
+ {
+ foreach (var element in _elements)
+ element.IsSelected = !element.IsSelected;
+ };
+ selectButtons.Add(toggleSelections);
+
+ var reloadButton = new Button();
+ reloadButton.text = "Refresh";
+ reloadButton.style.flexGrow = 1;
+ reloadButton.clicked += () =>
+ {
+ Reload();
+ };
+ selectButtons.Add(reloadButton);
+
+ view.Add(selectButtons);
+
+ // Load all modular shaders
+ _elements = new List<ModularShadersGeneratorElement>();
+ foreach (var modularShader in FindAssetsByType<ModularShader>())
+ {
+ var element = new ModularShadersGeneratorElement(modularShader);
+ _elements.Add(element);
+ view.Add(element);
+ }
+
+ var fileSelector = new VisualElement();
+ fileSelector.AddToClassList("folder-selector");
+
+ var folder = new TextField();
+ folder.value = _folderPath;
+ folder.RegisterValueChangedCallback(evt => _folderPath = evt.newValue);
+ folder.style.flexShrink = 1;
+ folder.style.flexGrow = 1;
+ folder.SetEnabled(false);
+ var label = new Label("Destination ");
+ label.style.alignSelf = Align.Center;
+ fileSelector.Add(label);
+ fileSelector.Add(folder);
+ var fileButton = new Button();
+ fileButton.text = "Open";
+ fileButton.clicked += () =>
+ {
+ string path = EditorUtility.OpenFolderPanel("Select folder to use", "Assets", "");
+ if (path.Length == 0)
+ return;
+
+ if (!Directory.Exists(path))
+ {
+ EditorUtility.DisplayDialog("Error", "The folder does not exist", "Ok");
+ return;
+ }
+
+ folder.value = path;
+ };
+ fileSelector.Add(fileButton);
+ view.Add(fileSelector);
+
+ var button = new Button();
+ button.text = "Generate Shaders";
+ button.clicked += GenerateShaders;
+ view.Add(button);
+
+ _root.Add(view);
+ }
+
+ private void GenerateShaders()
+ {
+ if (!Directory.Exists(_folderPath))
+ {
+ EditorUtility.DisplayDialog("Error", "The destination folder does not exist", "Ok");
+ return;
+ }
+
+ foreach (ModularShadersGeneratorElement element in _elements.Where(x => x.IsSelected))
+ ShaderGenerator.GenerateShader(_folderPath, element.Shader);
+ }
+
+ private static T[] FindAssetsByType<T>() where T : UnityEngine.Object
+ {
+ List<T> assets = new List<T>();
+ AssetDatabase.Refresh();
+ string[] guids = AssetDatabase.FindAssets($"t:{typeof(T).ToString().Replace("UnityEngine.", "")}");
+ for (int i = 0; i < guids.Length; i++)
+ {
+ string assetPath = AssetDatabase.GUIDToAssetPath(guids[i]);
+ T asset = AssetDatabase.LoadAssetAtPath<T>(assetPath);
+ if (asset != null)
+ assets.Add(asset);
+ }
+ return assets.ToArray();
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/ModularShadersGeneratorWindow.cs.meta b/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/ModularShadersGeneratorWindow.cs.meta
new file mode 100644
index 00000000..05c59212
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/ModularShadersGeneratorWindow.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 628a5d8b8b9a456ca963d43c28fb86c1
+timeCreated: 1638402450 \ No newline at end of file
diff --git a/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/PoiData.cs b/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/PoiData.cs
new file mode 100644
index 00000000..7b388701
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/PoiData.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using UnityEditor;
+using UnityEngine;
+
+namespace Poi
+{
+ internal static class PoiPaths
+ {
+ public const string defaultResourcesPath = "Library/unity default resources/";
+ public const string poiResourcesPath = "Poi/";
+ }
+
+ internal static class PoiStyles
+ {
+ public static GUIStyle BigButton
+ {
+ get
+ {
+ if(_bigButton == null)
+ _bigButton = new GUIStyle("button")
+ {
+ fixedHeight = 22 * EditorGUIUtility.pixelsPerPoint
+ };
+ return _bigButton;
+ }
+ }
+
+ public static GUIStyle TitleLabel
+ {
+ get
+ {
+ if(_titleLabel == null)
+ _titleLabel = new GUIStyle(EditorStyles.label)
+ {
+ fontSize = 15, stretchHeight = true, clipping = TextClipping.Overflow
+ };
+
+ return _titleLabel;
+ }
+ }
+
+ static GUIStyle _bigButton;
+ static GUIStyle _titleLabel;
+ }
+
+ internal static class PoiIcons
+ {
+ public static Texture2D LinkIcon
+ {
+ get
+ {
+ if(!_linkIcon)
+ {
+ string linkTexPath = EditorGUIUtility.isProSkin ? "icon_link_pro" : "icon_link";
+ _linkIcon = Resources.Load<Texture2D>(PoiPaths.poiResourcesPath + linkTexPath);
+ }
+
+ return _linkIcon;
+ }
+ }
+
+ private static Texture2D _linkIcon;
+ }
+}
+
+
diff --git a/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/PoiData.cs.meta b/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/PoiData.cs.meta
new file mode 100644
index 00000000..2fae90dc
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/PoiData.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 8f58036675b906e4797a5c394781b2a0
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/PoiHelpers.cs b/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/PoiHelpers.cs
new file mode 100644
index 00000000..2d3d1485
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/PoiHelpers.cs
@@ -0,0 +1,190 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using UnityEditor;
+using UnityEngine;
+
+namespace Poi
+{
+ static class PoiHelpers
+ {
+ static readonly string suffixSeparator = "_";
+
+ /// <summary>
+ /// Changes a path in Assets to an absolute windows path
+ /// </summary>
+ /// <param name="localPath"></param>
+ /// <returns></returns>
+ public static string LocalAssetsPathToAbsolutePath(string localPath)
+ {
+ localPath = NormalizePathSlashes(localPath);
+ const string assets = "Assets/";
+ if(localPath.StartsWith(assets))
+ {
+ localPath = localPath.Remove(0, assets.Length);
+ localPath = $"{Application.dataPath}/{localPath}";
+ }
+ return localPath;
+ }
+
+ /// <summary>
+ /// Replaces all forward slashes \ with back slashes /
+ /// </summary>
+ /// <param name="path"></param>
+ /// <returns></returns>
+ public static string NormalizePathSlashes(string path)
+ {
+ if(!string.IsNullOrEmpty(path))
+ path = path.Replace('\\', '/');
+ return path;
+ }
+
+ /// <summary>
+ /// Ensures directory exists inside the assets folder
+ /// </summary>
+ /// <param name="assetPath"></param>
+ public static void EnsurePathExistsInAssets(string assetPath)
+ {
+ Directory.CreateDirectory(LocalAssetsPathToAbsolutePath(assetPath));
+ }
+
+ /// <summary>
+ /// Adds a suffix to the end of the string then returns it
+ /// </summary>
+ /// <param name="str"></param>
+ /// <param name="suffixes"></param>
+ /// <returns></returns>
+ public static string AddSuffix(string str, params string[] suffixes)
+ {
+ bool ignoreSeparatorOnce = string.IsNullOrWhiteSpace(str);
+ StringBuilder sb = new StringBuilder(str);
+ foreach(var suff in suffixes)
+ {
+ if(ignoreSeparatorOnce)
+ {
+ sb.Append(suff);
+ ignoreSeparatorOnce = false;
+ continue;
+ }
+ sb.Append(suffixSeparator + suff);
+ }
+ return sb.ToString();
+ }
+
+ /// <summary>
+ /// Draws a GUI ilne
+ /// </summary>
+ /// <param name="spaceBefore"></param>
+ /// <param name="spaceAfter"></param>
+ internal static void DrawLine(bool spaceBefore = true, bool spaceAfter = true)
+ {
+ float spaceHeight = 3f;
+ if(spaceBefore)
+ GUILayout.Space(spaceHeight);
+
+ Rect rect = EditorGUILayout.GetControlRect(false, 1);
+ rect.height = 1;
+ EditorGUI.DrawRect(rect, new Color(0.5f, 0.5f, 0.5f, 1));
+
+ if(spaceAfter)
+ GUILayout.Space(spaceHeight);
+ }
+
+ /// <summary>
+ /// Destroys an object with DestroyImmediate in object mode and Destroy in play mode
+ /// </summary>
+ /// <param name="obj"></param>
+ internal static void DestroyAppropriate(UnityEngine.Object obj)
+ {
+ if(EditorApplication.isPlaying)
+ UnityEngine.Object.Destroy(obj);
+ else
+ UnityEngine.Object.DestroyImmediate(obj);
+ }
+
+ /// <summary>
+ /// Changes path from full windows path to a local path in the Assets folder
+ /// </summary>
+ /// <param name="path"></param>
+ /// <returns>Path starting with Assets</returns>
+ internal static string AbsolutePathToLocalAssetsPath(string path)
+ {
+ if(path.StartsWith(Application.dataPath))
+ path = "Assets" + path.Substring(Application.dataPath.Length);
+ return path;
+ }
+
+ /// <summary>
+ /// Selects and highlights the asset in your unity Project tab
+ /// </summary>
+ /// <param name="path"></param>
+ internal static void PingAssetAtPath(string path)
+ {
+ var inst = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(path).GetInstanceID();
+ EditorGUIUtility.PingObject(inst);
+ }
+
+ internal static void DrawWithLabelWidth(float width, Action action)
+ {
+ if(action == null)
+ return;
+ float old = EditorGUIUtility.labelWidth;
+ action.Invoke();
+ EditorGUIUtility.labelWidth = old;
+ }
+ }
+
+ internal static class PoiExtensions
+ {
+ public static Shader PackerShader => Shader.Find("Hidden/Poi/TexturePacker");
+
+ public static Shader UnpackerShader => Shader.Find("Hidden/Poi/TextureUnpacker");
+
+ /// <summary>
+ /// Extension method that bakes a material to <paramref name="tex"/>
+ /// </summary>
+ /// <param name="tex">Texture to bake <paramref name="materialToBake"/> to</param>
+ /// <param name="materialToBake">Material to bake to <paramref name="tex"/></param>
+ internal static void BakeMaterialToTexture(this Texture2D tex, Material materialToBake)
+ {
+ var res = new Vector2Int(tex.width, tex.height);
+
+ RenderTexture renderTexture = RenderTexture.GetTemporary(res.x, res.y);
+ Graphics.Blit(null, renderTexture, materialToBake);
+
+ //transfer image from rendertexture to texture
+ RenderTexture.active = renderTexture;
+ tex.ReadPixels(new Rect(Vector2.zero, res), 0, 0);
+ tex.Apply(false, false);
+
+ //clean up variables
+ RenderTexture.active = null;
+ RenderTexture.ReleaseTemporary(renderTexture);
+ }
+
+ /// <summary>
+ /// Rounds vector to closest power of two. Optionally, if above ceiling, square root down by one power of two
+ /// </summary>
+ /// <param name="vec"></param>
+ /// <param name="ceiling">Power of two ceiling. Will be rounded to power of two if not power of two already</param>
+ /// <returns></returns>
+ internal static Vector2Int ClosestPowerOfTwo(this Vector2Int vec, int? ceiling = null)
+ {
+ int x = Mathf.ClosestPowerOfTwo(vec.x);
+ int y = Mathf.ClosestPowerOfTwo(vec.y);
+
+ if(ceiling != null)
+ {
+ int ceil = Mathf.ClosestPowerOfTwo((int) ceiling);
+
+ x = Mathf.Clamp(x, x, ceil);
+ y = Mathf.Clamp(y, y, ceil);
+ }
+
+ return new Vector2Int(x, y);
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/PoiHelpers.cs.meta b/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/PoiHelpers.cs.meta
new file mode 100644
index 00000000..5326296d
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/PoiHelpers.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 32406f186e960c04ab7448ec0b4ca0e0
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/TextureUtility.cs b/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/TextureUtility.cs
new file mode 100644
index 00000000..c0d07d04
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/TextureUtility.cs
@@ -0,0 +1,1244 @@
+using System.Collections.Generic;
+using UnityEditor;
+using UnityEngine;
+using System.Linq;
+
+//Made by Dreadrith#3238
+//Server: https://discord.gg/ZsPfrGn
+//Github: https://github.com/Dreadrith/DreadScripts
+//Gumroad: https://gumroad.com/dreadrith
+
+namespace Poi.TextureUtility
+{
+ public class TextureUtility : EditorWindow
+ {
+ private readonly string[] DimensionPresets = new string[]
+ {
+ "128x128",
+ "256x256",
+ "512x512",
+ "1024x1024",
+ "2048x2048",
+ "4096x4096",
+ };
+
+ private static GUIContent resetIcon;
+ private static Texture2D titleTexture;
+
+ private static Texture2D mainTexture;
+ private static Texture2D maskTexture;
+
+ private static int jpgQuality = 75;
+ private static int texWidth, texHeight;
+ private static bool copyImport = true;
+ private static bool pingTexture = true;
+
+ private static bool rotating;
+ private static TexRotation rotationType;
+
+ private static bool inverting;
+ private static bool maskInvert=true;
+ private static bool invertRedS = true, invertGreenS = true, invertBlueS = true, invertAlphaS;
+
+ private static bool unpacking=true,packing;
+ private static bool editingTab=true, packingTab,creatingTab;
+
+
+ private static ChannelTexture redChannel = new ChannelTexture("Red", 0);
+ private static ChannelTexture greenChannel = new ChannelTexture("Green", 1);
+ private static ChannelTexture blueChannel = new ChannelTexture("Blue", 2);
+ private static ChannelTexture alphaChannel = new ChannelTexture("Alpha", 0);
+ private static ChannelTexture[] channelTextures = new ChannelTexture[] {redChannel,greenChannel,blueChannel,alphaChannel };
+
+ private static bool hueShifting;
+ private static bool maskHueShift=true;
+ private static float hueShiftFloat;
+
+ private static bool saturating;
+ private static bool maskSaturate=true;
+ private static float saturationFloat;
+
+ private static bool colorizing;
+ private static bool maskColorize=true;
+ private static bool textureColorize;
+ private static bool alphaColorize;
+ private static float colorizeFloat=0.5f;
+ private static Color colorizeColor = Color.black;
+ private static Texture2D colorizeTexture;
+
+ private static Color originalGUIColor;
+
+ private static TexEncoding encoding = TexEncoding.SaveAsPNG;
+ public enum TexEncoding
+ {
+ SaveAsPNG,
+ SaveAsJPG,
+ SaveAsTGA
+ }
+
+ public enum TexRotation
+ {
+ Clockwise90,
+ CClockwise90,
+ Rotate180,
+ FlipHorizontal,
+ FlipVertical
+ }
+
+ #region Creating Tab Variables
+ private static bool creatingCustomSize;
+ private static bool creatingReverse;
+ private static string creatingPath;
+ private static Color solidColor=Color.black;
+ private static Gradient gradientColor = new Gradient() { colorKeys = new GradientColorKey[] { new GradientColorKey(Color.white, 0), new GradientColorKey(Color.black, 1) } };
+
+ private static TextureCreatingMode creatingMode = TextureCreatingMode.SolidColor;
+
+ private enum TextureCreatingMode
+ {
+ SolidColor,
+ HorizontalGradient,
+ VerticalGradient
+ }
+ #endregion
+
+ [MenuItem("Tools/Poi/Texture Utility")]
+ private static void showWindow()
+ {
+ EditorWindow w = GetWindow<TextureUtility>(false, "Texture Utility", true);
+ if (!titleTexture)
+ {
+ titleTexture = GetColors((Texture2D)EditorGUIUtility.IconContent("Texture2D Icon").image, 16, 16, out _);
+ titleTexture.Apply();
+ }
+
+ w.titleContent.image = titleTexture;
+ w.minSize = new Vector2(423, 253);
+ }
+
+ private void OnGUI()
+ {
+ originalGUIColor = GUI.backgroundColor;
+ using (new GUILayout.HorizontalScope())
+ {
+ bool c = editingTab;
+
+ SetColorIcon(editingTab);
+ editingTab = GUILayout.Toggle(editingTab, "Editing", "toolbarbutton");
+ if (!c && editingTab)
+ {
+ packingTab = false;
+ creatingTab = false;
+ }
+
+ c = creatingTab;
+
+
+ SetColorIcon(creatingTab);
+ creatingTab = GUILayout.Toggle(creatingTab, "Creating", "toolbarbutton");
+ if (!c && creatingTab)
+ {
+ packingTab = false;
+ editingTab = false;
+ }
+
+ c = packingTab;
+
+ SetColorIcon(packingTab);
+ packingTab = GUILayout.Toggle(packingTab, "Packing", "toolbarbutton");
+ if (!c && packingTab)
+ {
+ editingTab = false;
+ creatingTab = false;
+ }
+ GUI.backgroundColor = originalGUIColor;
+ }
+
+ if (editingTab)
+ {
+ DrawEditingTab();
+ }
+
+ if (creatingTab)
+ {
+ DrawCreatingTab();
+ }
+
+ if (packingTab)
+ {
+ DrawPackingTab();
+ }
+ Credit();
+ }
+
+
+ private void DrawEditingTab()
+ {
+ using (new GUILayout.HorizontalScope())
+ {
+ using (new GUILayout.VerticalScope())
+ {
+ using (new GUILayout.HorizontalScope("box"))
+ DrawDimensionsGUI();
+
+ using (new GUILayout.HorizontalScope("box"))
+ {
+ encoding = (TexEncoding)EditorGUILayout.EnumPopup(encoding, GUILayout.Width(95));
+
+ EditorGUI.BeginDisabledGroup(encoding != TexEncoding.SaveAsJPG);
+ EditorGUIUtility.labelWidth = 50;
+ jpgQuality = EditorGUILayout.IntSlider("Quality", jpgQuality, 1, 100);
+ EditorGUIUtility.labelWidth = 0;
+ EditorGUI.EndDisabledGroup();
+ }
+
+ using (new GUILayout.HorizontalScope("box"))
+ {
+ copyImport = EditorGUILayout.Toggle("Copy Import Settings", copyImport);
+ pingTexture = EditorGUILayout.Toggle(new GUIContent("Highlight Texture", "Highlight the newly created texture in Assets"), pingTexture);
+ }
+
+ using (new GUILayout.HorizontalScope("box"))
+ {
+ if (!rotating)
+ {
+ SetColorIcon(rotating);
+ rotating = GUILayout.Toggle(rotating, "Rotate", "toolbarbutton");
+ GUI.backgroundColor = originalGUIColor;
+ }
+ else
+ {
+ SetColorIcon(rotating);
+ rotating = GUILayout.Toggle(rotating, "", "toolbarbutton", GUILayout.Width(17), GUILayout.Height(17));
+ GUI.backgroundColor = originalGUIColor;
+
+ EditorGUI.BeginDisabledGroup(true);
+ GUILayout.Toggle(true, "M", EditorStyles.miniButton, GUILayout.Width(21), GUILayout.Height(16));
+ EditorGUI.EndDisabledGroup();
+
+ GUILayout.Label("Rotate");
+ rotationType = (TexRotation)EditorGUILayout.EnumPopup(GUIContent.none, rotationType);
+ }
+ }
+
+ using (new GUILayout.HorizontalScope("box"))
+ {
+ if (!inverting)
+ {
+ SetColorIcon(inverting);
+ inverting = GUILayout.Toggle(inverting, "Invert", "toolbarbutton");
+ GUI.backgroundColor = originalGUIColor;
+ }
+ else
+ {
+ SetColorIcon(inverting);
+ inverting = GUILayout.Toggle(inverting, "", "toolbarbutton", GUILayout.Width(17), GUILayout.Height(17));
+ GUI.backgroundColor = originalGUIColor;
+
+ maskInvert = GUILayout.Toggle(maskInvert, new GUIContent("M", "Use Mask"), EditorStyles.miniButton, GUILayout.Width(21), GUILayout.Height(16)); GUILayout.Label("Invert");
+ invertRedS = EditorGUILayout.ToggleLeft("R", invertRedS, GUILayout.Width(30));
+ invertGreenS = EditorGUILayout.ToggleLeft("G", invertGreenS, GUILayout.Width(30));
+ invertBlueS = EditorGUILayout.ToggleLeft("B", invertBlueS, GUILayout.Width(30));
+ invertAlphaS = EditorGUILayout.ToggleLeft("A", invertAlphaS, GUILayout.Width(30));
+
+ }
+ }
+
+ using (new GUILayout.HorizontalScope("box"))
+ {
+ if (!saturating)
+ {
+ SetColorIcon(saturating);
+ saturating = GUILayout.Toggle(saturating, "Saturate", "toolbarbutton");
+ GUI.backgroundColor = originalGUIColor;
+ }
+ else
+ {
+ SetColorIcon(saturating);
+ saturating = GUILayout.Toggle(saturating, "", "toolbarbutton", GUILayout.Width(17), GUILayout.Height(17));
+ GUI.backgroundColor = originalGUIColor;
+ maskSaturate = GUILayout.Toggle(maskSaturate, new GUIContent("M", "Use Mask"), EditorStyles.miniButton, GUILayout.Width(21), GUILayout.Height(16));
+ EditorGUIUtility.labelWidth = 65;
+ saturationFloat = EditorGUILayout.Slider("Saturate", saturationFloat, -1, 1);
+ EditorGUIUtility.labelWidth = 0;
+ }
+ }
+ using (new GUILayout.HorizontalScope("box"))
+ {
+ if (!hueShifting)
+ {
+ SetColorIcon(hueShifting);
+ hueShifting = GUILayout.Toggle(hueShifting, "Hue Shift", "toolbarbutton");
+ GUI.backgroundColor = originalGUIColor;
+ }
+ else
+ {
+ SetColorIcon(hueShifting);
+ hueShifting = GUILayout.Toggle(hueShifting, "", "toolbarbutton", GUILayout.Width(17), GUILayout.Height(17));
+ GUI.backgroundColor = originalGUIColor;
+
+ maskHueShift = GUILayout.Toggle(maskHueShift, new GUIContent("M", "Use Mask"), EditorStyles.miniButton, GUILayout.Width(21), GUILayout.Height(16));
+ EditorGUIUtility.labelWidth = 65;
+ hueShiftFloat = EditorGUILayout.Slider("Hue Shift", hueShiftFloat, 0, 1);
+ EditorGUIUtility.labelWidth = 0;
+ }
+ }
+
+ using (new GUILayout.HorizontalScope("box"))
+ {
+ if (!colorizing)
+ {
+ SetColorIcon(colorizing);
+ colorizing = GUILayout.Toggle(colorizing, "Colorize", "toolbarbutton");
+ GUI.backgroundColor = originalGUIColor;
+ }
+ else
+ {
+ SetColorIcon(colorizing);
+ colorizing = GUILayout.Toggle(colorizing, "", "toolbarbutton", GUILayout.Width(17), GUILayout.Height(17));
+ GUI.backgroundColor = originalGUIColor;
+
+ maskColorize = GUILayout.Toggle(maskColorize, new GUIContent("M", "Use Mask"), EditorStyles.miniButton, GUILayout.Width(21), GUILayout.Height(16));
+ EditorGUIUtility.labelWidth = 65;
+ colorizeFloat = EditorGUILayout.Slider("Colorize", colorizeFloat, 0, 1);
+ EditorGUIUtility.labelWidth = 0;
+ if (!textureColorize)
+ colorizeColor = EditorGUILayout.ColorField(new GUIContent(""), colorizeColor, true, alphaColorize, false, GUILayout.Width(70), GUILayout.Height(17));
+ else
+ colorizeTexture = (Texture2D)EditorGUILayout.ObjectField(colorizeTexture, typeof(Texture2D), false, GUILayout.Width(70), GUILayout.Height(17));
+ textureColorize = GUILayout.Toggle(textureColorize, new GUIContent("T", "Use Texture"), EditorStyles.miniButton, GUILayout.Width(19), GUILayout.Height(16));
+ alphaColorize = GUILayout.Toggle(alphaColorize, new GUIContent("A", "Use Alpha"), EditorStyles.miniButton, GUILayout.Width(19), GUILayout.Height(16));
+ }
+ }
+
+ }
+ using (new GUILayout.VerticalScope())
+ {
+ using (new GUILayout.VerticalScope("box"))
+ {
+ EditorGUIUtility.labelWidth = 1;
+ GUILayout.Label("Main", GUILayout.Width(65));
+ EditorGUI.BeginChangeCheck();
+ mainTexture = (Texture2D)EditorGUILayout.ObjectField("", mainTexture, typeof(Texture2D), false, GUILayout.Width(65));
+ if (EditorGUI.EndChangeCheck())
+ ResetDimensions();
+ EditorGUIUtility.labelWidth = 0;
+ }
+ EditorGUI.BeginDisabledGroup(!(hueShifting || saturating || inverting || colorizing));
+ using (new GUILayout.VerticalScope("box"))
+ {
+ EditorGUIUtility.labelWidth = 1;
+ GUILayout.Label("Mask", GUILayout.Width(65));
+ maskTexture = (Texture2D)EditorGUILayout.ObjectField("", maskTexture, typeof(Texture2D), false, GUILayout.Width(65));
+ EditorGUIUtility.labelWidth = 0;
+ }
+ EditorGUI.EndDisabledGroup();
+ }
+ }
+ EditorGUI.BeginDisabledGroup(!mainTexture);
+ if (GUILayout.Button("Apply"))
+ {
+ ApplyTexture();
+ }
+ EditorGUI.EndDisabledGroup();
+ }
+
+ private void DrawCreatingTab()
+ {
+ using (new GUILayout.HorizontalScope("box"))
+ {
+ if (!creatingCustomSize)
+ {
+ SetColorIcon(creatingCustomSize);
+ creatingCustomSize = GUILayout.Toggle(inverting, "Custom Dimensions", "toolbarbutton");
+ GUI.backgroundColor = originalGUIColor;
+ }
+ else
+ {
+ SetColorIcon(creatingCustomSize);
+ creatingCustomSize = GUILayout.Toggle(creatingCustomSize, "", "toolbarbutton", GUILayout.Width(17), GUILayout.Height(17));
+ GUI.backgroundColor = originalGUIColor;
+
+ DrawDimensionsGUI(false);
+ }
+ }
+
+ using (new GUILayout.HorizontalScope("box"))
+ {
+ encoding = (TexEncoding)EditorGUILayout.EnumPopup(encoding);
+
+ EditorGUI.BeginDisabledGroup(encoding != TexEncoding.SaveAsJPG);
+ EditorGUIUtility.labelWidth = 50;
+ jpgQuality = EditorGUILayout.IntSlider("Quality", jpgQuality, 1, 100);
+ EditorGUIUtility.labelWidth = 0;
+ EditorGUI.EndDisabledGroup();
+ }
+
+ using (new GUILayout.HorizontalScope("box"))
+ {
+ pingTexture = EditorGUILayout.Toggle(new GUIContent("Highlight Texture", "Highlight the newly created texture in Assets"), pingTexture);
+
+ EditorGUI.BeginDisabledGroup(creatingMode != TextureCreatingMode.HorizontalGradient && creatingMode != TextureCreatingMode.VerticalGradient);
+ creatingReverse = EditorGUILayout.Toggle("Reverse Texture", creatingReverse);
+ EditorGUI.EndDisabledGroup();
+ }
+
+ using (new GUILayout.HorizontalScope("box"))
+ {
+ creatingMode = (TextureCreatingMode)EditorGUILayout.EnumPopup("Texture Mode", creatingMode);
+ }
+
+ switch ((int)creatingMode)
+ {
+ case 0:
+ solidColor = EditorGUILayout.ColorField(solidColor);
+ break;
+ case 1:
+ case 2:
+ gradientColor = EditorGUILayout.GradientField(gradientColor);
+ break;
+ }
+ if (GUILayout.Button("Create"))
+ {
+ CreateTexture();
+ }
+ AssetFolderPath(ref creatingPath, "Save To", "TextureUtilityCreatingPath");
+ }
+
+ private void DrawPackingTab()
+ {
+
+ using (new GUILayout.HorizontalScope("box"))
+ {
+ encoding = (TexEncoding)EditorGUILayout.EnumPopup(encoding);
+ EditorGUI.BeginDisabledGroup(encoding != TexEncoding.SaveAsJPG);
+ EditorGUIUtility.labelWidth = 50;
+ jpgQuality = EditorGUILayout.IntSlider("Quality", jpgQuality, 1, 100);
+ EditorGUIUtility.labelWidth = 0;
+ EditorGUI.EndDisabledGroup();
+ }
+ using (new GUILayout.HorizontalScope("box"))
+ {
+ copyImport = EditorGUILayout.Toggle("Copy Import Settings", copyImport);
+ pingTexture = EditorGUILayout.Toggle(new GUIContent("Highlight Texture", "Highlight the newly created texture in Assets"), pingTexture);
+ }
+ using (new GUILayout.HorizontalScope())
+ {
+ bool p = unpacking;
+ SetColorIcon(unpacking);
+ unpacking = GUILayout.Toggle(unpacking, "Unpack", "toolbarbutton");
+ if (!p && unpacking)
+ packing = false;
+
+ p = packing;
+ SetColorIcon(packing);
+ packing = GUILayout.Toggle(packing, "Pack", "toolbarbutton");
+ if (!p && packing)
+ unpacking = false;
+
+ GUI.backgroundColor = originalGUIColor;
+ }
+ if (packing)
+ {
+ using (new GUILayout.HorizontalScope())
+ {
+ EditorGUIUtility.labelWidth = 1;
+ redChannel.DrawGUI();
+ greenChannel.DrawGUI();
+ blueChannel.DrawGUI();
+ alphaChannel.DrawGUI();
+ EditorGUIUtility.labelWidth = 0;
+ }
+ EditorGUI.BeginDisabledGroup(!channelTextures.Any(c => c.texture));
+ if (GUILayout.Button("Pack"))
+ {
+ PackTexture(channelTextures);
+ }
+ }
+ if (unpacking)
+ {
+
+ using (new GUILayout.VerticalScope("box"))
+ {
+ using (new GUILayout.HorizontalScope())
+ {
+ GUILayout.FlexibleSpace();
+ GUILayout.Label("Main Texture");
+ GUILayout.FlexibleSpace();
+ }
+
+ using (new GUILayout.HorizontalScope())
+ {
+ GUILayout.FlexibleSpace();
+ EditorGUIUtility.labelWidth = 1;
+ mainTexture = (Texture2D)EditorGUILayout.ObjectField("", mainTexture, typeof(Texture2D), false, GUILayout.Width(66));
+ EditorGUIUtility.labelWidth = 0;
+ GUILayout.FlexibleSpace();
+ }
+ }
+ EditorGUI.BeginDisabledGroup(!mainTexture);
+ if (GUILayout.Button("Unpack"))
+ {
+ UnpackTexture();
+ }
+ EditorGUI.EndDisabledGroup();
+ }
+
+
+ EditorGUI.EndDisabledGroup();
+ }
+
+ private void DrawDimensionsGUI(bool drawReset=true)
+ {
+ GUIStyle iconStyle = new GUIStyle(GUI.skin.label) { padding = new RectOffset(), margin = new RectOffset(), imagePosition = ImagePosition.ImageOnly };
+
+ EditorGUI.BeginDisabledGroup(!mainTexture && !creatingTab);
+ if (drawReset)
+ {
+ if (GUILayout.Button(resetIcon, iconStyle, GUILayout.Height(16), GUILayout.Width(16)))
+ ResetDimensions();
+ }
+ EditorGUIUtility.labelWidth = 20;
+ texWidth = EditorGUILayout.IntField(new GUIContent("W","Width"), texWidth);
+ texHeight = EditorGUILayout.IntField(new GUIContent("H", "Height"), texHeight);
+ EditorGUIUtility.labelWidth = 0;
+
+ int dummy = -1;
+ EditorGUI.BeginChangeCheck();
+ dummy = EditorGUILayout.Popup(dummy, DimensionPresets,GUILayout.Width(17));
+ if (EditorGUI.EndChangeCheck())
+ {
+ string[] dimensions = ((string)DimensionPresets.GetValue(dummy)).Split('x');
+ texWidth = int.Parse(dimensions[0]);
+ texHeight = int.Parse(dimensions[1]);
+ }
+
+ EditorGUI.EndDisabledGroup();
+
+ }
+
+ public static Texture2D GetColors(Texture2D texture, out Color[] Colors, bool unloadTempTexture = false)
+ {
+ return GetColors(texture, texture.width, texture.height, out Colors, unloadTempTexture);
+ }
+
+ public static Texture2D GetColors(Texture2D texture, int width, int height, out Color[] Colors,bool unloadTempTexture = false)
+ {
+ //Thanks to
+ //https://gamedev.stackexchange.com/questions/92285/unity3d-resize-texture-without-corruption
+ texture.filterMode = FilterMode.Point;
+ RenderTexture rt = RenderTexture.GetTemporary(width, height);
+
+ rt.filterMode = FilterMode.Point;
+ RenderTexture.active = rt;
+ Graphics.Blit(texture, rt);
+ Texture2D newTexture = new Texture2D(width, height);
+ newTexture.ReadPixels(new Rect(0, 0, width, height), 0, 0);
+ Color[] myColors = newTexture.GetPixels();
+ RenderTexture.active = null;
+ /////////////////////
+ Colors = myColors;
+ if (unloadTempTexture)
+ {
+ DestroyImmediate(newTexture);
+ return null;
+ }
+ return newTexture;
+ }
+
+ private static void SaveTexture(byte[] textureEncoding, string path, bool refresh=false, bool ping=false)
+ {
+ System.IO.FileStream stream = System.IO.File.Create(path);
+ stream.Write(textureEncoding, 0, textureEncoding.Length);
+ stream.Dispose();
+ if (refresh)
+ {
+ AssetDatabase.Refresh();
+ if (ping)
+ {
+ Ping(path);
+ }
+ }
+
+ }
+ private static void Ping(string path)
+ {
+ EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath<Object>(path));
+ }
+
+ private void ApplyTexture()
+ {
+ if (colorizing && !colorizeTexture && textureColorize)
+ {
+ Debug.LogError("Cannot Colorize using a texture without a texture!");
+ return;
+ }
+
+ string destinationPath = GetDestinationFolder(mainTexture);
+ string texPath = AssetDatabase.GetAssetPath(mainTexture);
+
+ Texture2D newTexture = GetColors(mainTexture, texWidth, texHeight, out Color[] myColors);
+
+ if (rotating)
+ {
+ List<Color> rotatedColors = new List<Color>();
+ switch (rotationType)
+ {
+ case TexRotation.Clockwise90:
+ for (int i = texWidth-1; i >=0; i--)
+ {
+ rotatedColors.AddRange(newTexture.GetPixels(i, 0, 1, texHeight));
+ }
+ myColors = rotatedColors.ToArray();
+ newTexture = new Texture2D(texHeight, texWidth);
+ break;
+
+ case TexRotation.CClockwise90:
+ for (int i = 0; i < texWidth; i++)
+ {
+ rotatedColors.AddRange(ReverseArray(newTexture.GetPixels(i, 0, 1, texHeight)));
+ }
+ myColors = rotatedColors.ToArray();
+ newTexture = new Texture2D(texHeight, texWidth);
+ break;
+
+ case TexRotation.Rotate180:
+ myColors = ReverseArray(myColors);
+ break;
+
+ case TexRotation.FlipHorizontal:
+ for (int i = 0; i < texHeight; i++)
+ {
+ rotatedColors.AddRange(ReverseArray(newTexture.GetPixels(0, i, texWidth, 1)));
+ }
+ myColors = rotatedColors.ToArray();
+ break;
+
+ case TexRotation.FlipVertical:
+ for (int i = texHeight - 1; i >= 0; i--)
+ {
+ rotatedColors.AddRange(newTexture.GetPixels(0, i, texWidth, 1));
+ }
+ myColors = rotatedColors.ToArray();
+ break;
+ }
+
+ }
+
+ bool colorInverting = (invertRedS || invertGreenS || invertBlueS || invertAlphaS) && inverting;
+ bool HSVEditing = hueShifting || saturating;
+ bool colorEditing = HSVEditing || colorizing;
+ bool editing = colorEditing || colorInverting || unpacking;
+ bool masking = ((maskColorize && colorizing) || (maskInvert && colorInverting) || (maskSaturate && saturating) || (maskHueShift && hueShifting)) && maskTexture;
+
+ Color[] maskColors;
+ if (masking)
+ {
+ GetColors(maskTexture, texWidth, texHeight, out maskColors, true);
+ }
+ else
+ maskColors = null;
+
+ Color[] colorizeTextureColors;
+ if (colorizing && textureColorize)
+ {
+ GetColors(colorizeTexture, texWidth, texHeight, out colorizeTextureColors, true);
+ }
+ else
+ colorizeTextureColors = null;
+
+
+ Color[] newColors = new Color[myColors.Length];
+ if (editing)
+ {
+ for (int i = 0; i < myColors.Length; i++)
+ {
+ Color currentColor = myColors[i];
+
+ if (colorEditing)
+ {
+ if (HSVEditing)
+ {
+ Color.RGBToHSV(currentColor, out float h, out float s, out float v);
+ currentColor = Color.HSVToRGB(hueShifting ? (Mathf.Repeat(h + (hueShiftFloat * (maskTexture && maskHueShift ? maskColors[i].r : 1)), 1)) : h, saturating ? (Mathf.Clamp01(s + (saturationFloat * (maskTexture && maskSaturate ? maskColors[i].r : 1)))) : s, v);
+ currentColor.a = myColors[i].a;
+ }
+ if (colorizing)
+ {
+ float oga = currentColor.a;
+ currentColor = Color.Lerp(currentColor, textureColorize ? colorizeTextureColors[i] : colorizeColor, colorizeFloat * (maskColorize && maskTexture ? maskColors[i].r : 1));
+
+ if (!alphaColorize)
+ currentColor.a = oga;
+ }
+ }
+
+ float r = colorInverting && invertRedS ? currentColor.r - ((currentColor.r - (1 - currentColor.r)) * (maskInvert && maskTexture ? maskColors[i].r : 1)) : currentColor.r;
+ float g = colorInverting && invertGreenS ? currentColor.g - ((currentColor.g - (1 - currentColor.g)) * (maskInvert && maskTexture ? maskColors[i].g : 1)) : currentColor.g;
+ float b = colorInverting && invertBlueS ? currentColor.b - ((currentColor.b - (1 - currentColor.b)) * (maskInvert && maskTexture ? maskColors[i].b : 1)) : currentColor.b;
+ float a = colorInverting && invertAlphaS ? currentColor.a - ((currentColor.a - (1 - currentColor.a)) * (maskInvert && maskTexture ? maskColors[i].a : 1)) : currentColor.a;
+
+ newColors[i] = new Color(r, g, b, a);
+ }
+ }
+ newTexture.SetPixels(editing ? newColors : myColors);
+ newTexture.Apply();
+
+ GetEncoding(newTexture, encoding, out byte[] data, out string ext);
+
+ string newTexturePath = AssetDatabase.GenerateUniqueAssetPath(destinationPath + "/" + mainTexture.name
+ + (colorInverting ? " Inverted" : "") + ext);
+
+ SaveTexture(data, newTexturePath, true, pingTexture);
+
+ if (copyImport)
+ {
+ CopyTextureSettings(texPath, newTexturePath);
+ }
+ }
+
+ private static void GetEncoding(Texture2D texture, TexEncoding encodingType, out byte[] data, out string ext)
+ {
+ switch ((int)encodingType)
+ {
+ default:
+ ext = ".png";
+ data = texture.EncodeToPNG();
+ break;
+ case 1:
+ ext = ".jpg";
+ data = texture.EncodeToJPG(jpgQuality);
+ break;
+ case 2:
+ ext = ".tga";
+ data = texture.EncodeToTGA();
+ break;
+ }
+ }
+
+
+ private void CreateTexture()
+ {
+ Texture2D newTexture = null;
+ int w = creatingCustomSize ? texWidth : 0;
+ int h = creatingCustomSize ? texHeight : 0;
+
+ Color[] myColors = null;
+ switch ((int)creatingMode)
+ {
+ case 0:
+ if (!creatingCustomSize)
+ {
+ w = h = 4;
+ }
+ newTexture = new Texture2D(w, h);
+
+ myColors = CreateFilledArray(solidColor, w * h);
+ newTexture.SetPixels(0, 0, w, h, myColors);
+ break;
+ case 1:
+ {
+ if (!creatingCustomSize)
+ {
+ w = 256;
+ h = 4;
+ }
+ newTexture = new Texture2D(w, h);
+
+ int i = creatingReverse ? w - 1 : 0;
+ int istep = creatingReverse ? -1 : 1;
+
+ float xstepValue = (1f / w);
+ float xcurrentStep = 0;
+ for (; i < w && i >= 0; i += istep)
+ {
+ newTexture.SetPixels(i, 0, 1, h, CreateFilledArray(gradientColor.Evaluate(xcurrentStep), h));
+ xcurrentStep += xstepValue;
+ }
+ }
+ break;
+ case 2:
+ {
+ if (!creatingCustomSize)
+ {
+ w = 4;
+ h = 256;
+ }
+ newTexture = new Texture2D(w, h);
+
+ int i = creatingReverse ? h - 1 : 0;
+ int istep = creatingReverse ? -1 : 1;
+
+ float ystepValue = 1f / h;
+ float ycurrentStep = 0;
+ for (; i < h && i >= 0; i += istep)
+ {
+ newTexture.SetPixels(0, i, w, 1, CreateFilledArray(gradientColor.Evaluate(ycurrentStep), w));
+ ycurrentStep += ystepValue;
+ }
+ }
+ break;
+ }
+
+ GetEncoding(newTexture, encoding, out byte[] data, out string ext);
+
+ RecreateFolders(creatingPath);
+ SaveTexture(data, AssetDatabase.GenerateUniqueAssetPath(creatingPath +"/Generated Texture"+ext), true, pingTexture);
+ }
+
+ private void UnpackTexture()
+ {
+ string destinationPath = GetDestinationFolder(mainTexture);
+ string texPath = AssetDatabase.GetAssetPath(mainTexture);
+ int x = mainTexture.width, y = mainTexture.height;
+ Texture2D newTexture = GetColors(mainTexture, x, y, out Color[] myColors);
+ List<System.Tuple<string, string>> copyFromTo = new List<System.Tuple<string, string>>();
+
+ bool isRedPass = true, isGreenPass, isBluePass, isAlphaPass;
+ isGreenPass = isBluePass = isAlphaPass = false;
+ try
+ {
+ AssetDatabase.StartAssetEditing();
+
+ do
+ {
+ Color[] newColors = new Color[myColors.Length];
+
+ bool hasAlpha = false;
+ for (int i = 0; i < myColors.Length; i++)
+ {
+ Color currentColor = myColors[i];
+
+ float r = currentColor.r;
+ float g = currentColor.g;
+ float b = currentColor.b;
+ float a = currentColor.a;
+
+ if (isRedPass)
+ {
+ g = b = r;
+ a = 1;
+ }
+ if (isGreenPass)
+ {
+ r = b = g;
+ a = 1;
+ }
+ if (isBluePass)
+ {
+ r = g = b;
+ a = 1;
+ }
+ if (isAlphaPass)
+ {
+ r = g = b = a;
+ if (a != 1)
+ hasAlpha = true;
+ }
+
+ newColors[i] = new Color(r, g, b, a);
+ }
+
+ if (isAlphaPass && !hasAlpha)
+ {
+ isAlphaPass = false;
+ goto Skip;
+ }
+
+ newTexture.SetPixels(newColors);
+ newTexture.Apply();
+
+ GetEncoding(newTexture, encoding, out byte[] data, out string ext);
+
+ string newTexturePath = AssetDatabase.GenerateUniqueAssetPath(destinationPath + "/" + mainTexture.name
+ + (isRedPass ? "-Red" : isGreenPass ? "-Green" : isBluePass ? "-Blue" : "-Alpha") + ext);
+
+ SaveTexture(data, newTexturePath);
+
+ if (copyImport)
+ {
+ copyFromTo.Add(new System.Tuple<string, string>(texPath, newTexturePath));
+ }
+
+ if (isAlphaPass)
+ isAlphaPass = false;
+ if (isBluePass)
+ {
+ isBluePass = false;
+ isAlphaPass = true;
+ }
+ if (isGreenPass)
+ {
+ isGreenPass = false;
+ isBluePass = true;
+ }
+ if (isRedPass)
+ {
+ isRedPass = false;
+ isGreenPass = true;
+ }
+
+ if (unpacking)
+ newTexture = new Texture2D(x, y);
+
+ Skip:;
+
+ } while (isRedPass || isGreenPass || isBluePass || isAlphaPass);
+ }
+ finally
+ {
+ AssetDatabase.StopAssetEditing();
+ }
+ AssetDatabase.Refresh();
+ if (copyImport)
+ {
+ for (int i = 0; i < copyFromTo.Count; i++)
+ {
+ CopyTextureSettings(copyFromTo[i].Item1, copyFromTo[i].Item2);
+ }
+ }
+ }
+
+ public void PackTexture(ChannelTexture[] channels)
+ {
+ int firstIndex = 0;
+ for (int i = 3; i >= 0; i--)
+ {
+ if (channels[i].texture)
+ firstIndex = i;
+ }
+ ChannelTexture firstChannel = channels[firstIndex];
+ int w = firstChannel.texture.width;
+ int h = firstChannel.texture.height;
+ PackTexture(channels, AssetDatabase.GetAssetPath(firstChannel.texture), w, h, encoding);
+ }
+
+ public static string PackTexture(ChannelTexture[] channels, TexEncoding encodingType, bool refresh=true, bool copyImportSettings=true)
+ {
+ int firstIndex = -1;
+ for (int i = 3; i >= 0; i--)
+ {
+ if (channels[i].texture)
+ firstIndex = i;
+ }
+ if (firstIndex < 0)
+ return string.Empty;
+ ChannelTexture firstChannel = channels[firstIndex];
+ int w = firstChannel.texture.width;
+ int h = firstChannel.texture.height;
+ return PackTexture(channels, AssetDatabase.GetAssetPath(firstChannel.texture), w, h, encodingType,refresh,false,copyImportSettings);
+ }
+
+ public static string PackTexture(ChannelTexture[] channels, string destination,int width, int height, TexEncoding encodingType, bool refresh=true,bool overwrite=false, bool copyImportSettings=true)
+ {
+ int firstIndex = -1;
+ for (int i = 3; i >= 0; i--)
+ {
+ if (channels[i].texture)
+ firstIndex = i;
+ }
+ if (firstIndex < 0)
+ return string.Empty;
+
+ ChannelTexture firstChannel = channels[firstIndex];
+
+
+ Texture2D newTexture = new Texture2D(width, height);
+ channels[0].GetChannelColors(width, height, out float[] reds, true);
+ channels[1].GetChannelColors(width, height, out float[] greens, true);
+ channels[2].GetChannelColors(width, height, out float[] blues, true);
+ channels[3].GetChannelColors(width, height, out float[] alphas, true);
+ Color[] finalColors = new Color[width*height];
+
+ for (int i=0;i< finalColors.Length;i++)
+ {
+ finalColors[i].r = (reds!=null) ? reds[i] : 0;
+ finalColors[i].g = (greens != null) ? greens[i] : 0;
+ finalColors[i].b = (blues != null) ? blues[i] : 0;
+ finalColors[i].a = (alphas != null) ? alphas[i] : 1;
+ }
+ newTexture.SetPixels(finalColors);
+ newTexture.Apply();
+
+ GetEncoding(newTexture, encodingType, out byte[] data, out string ext);
+
+ string newTexturePath = GetDestinationFolder(destination)+"/"+System.IO.Path.GetFileNameWithoutExtension(destination)+ext;
+ if (!overwrite)
+ newTexturePath = AssetDatabase.GenerateUniqueAssetPath(newTexturePath);
+ SaveTexture(data, newTexturePath);
+ DestroyImmediate(newTexture);
+ if (refresh)
+ AssetDatabase.Refresh();
+
+
+ if (copyImportSettings)
+ {
+ CopyTextureSettings(AssetDatabase.GetAssetPath(firstChannel.texture), newTexturePath);
+ }
+ return newTexturePath;
+ }
+
+ private static void CopyTextureSettings(string from, string to)
+ {
+ TextureImporter source = (TextureImporter)AssetImporter.GetAtPath(from);
+ TextureImporterSettings sourceSettings = new TextureImporterSettings();
+ source.ReadTextureSettings(sourceSettings);
+
+ TextureImporter destination = (TextureImporter)AssetImporter.GetAtPath(to);
+ destination.SetTextureSettings(sourceSettings);
+ destination.maxTextureSize = source.maxTextureSize;
+ destination.textureCompression = source.textureCompression;
+ destination.crunchedCompression = source.crunchedCompression;
+ destination.SaveAndReimport();
+ }
+
+ private static string GetDestinationFolder(Object o)
+ {
+ string path = AssetDatabase.GetAssetPath(o);
+ return GetDestinationFolder(path);
+ }
+ private static string GetDestinationFolder(string path)
+ {
+ return path.Substring(0, path.LastIndexOf('/'));
+ }
+
+ private void ResetDimensions()
+ {
+ if (mainTexture)
+ {
+ texHeight = mainTexture.height;
+ texWidth = mainTexture.width;
+ }
+ }
+
+ private void SetColorIcon(bool value)
+ {
+ if (value)
+ GUI.backgroundColor = Color.green;
+ else
+ GUI.backgroundColor = Color.grey;
+ }
+
+ private void OnEnable()
+ {
+ resetIcon = new GUIContent(EditorGUIUtility.IconContent("d_Refresh")) { tooltip = "Reset Dimensions" };
+ creatingPath = PlayerPrefs.GetString("TextureUtilityCreatingPath", "Assets/DreadScripts/Texture Utility/Generated Assets");
+
+ for (int i=0;i<channelTextures.Length;i++)
+ {
+ channelTextures[i].SetMode(EditorPrefs.GetInt("TextureUtilityChannel" + channelTextures[i].name, (int)channelTextures[i].mode));
+ }
+ }
+
+ private static T[] CreateFilledArray<T>(T variable,int length)
+ {
+ T[] myArray = new T[length];
+ for (int i=0;i< myArray.Length;i++)
+ {
+ myArray[i] = variable;
+ }
+ return myArray;
+ }
+
+ private static T[] ReverseArray<T>(T[] array)
+ {
+ T[] reversed = new T[array.Length];
+ int index = array.Length - 1;
+ for (int i = 0; i < reversed.Length; i++)
+ {
+ reversed[i] = array[index];
+ index--;
+ }
+ return reversed;
+ }
+
+ #region Extracted From DS_CommonMethods
+ public static void AssetFolderPath(ref string variable, string title, string playerpref)
+ {
+ EditorGUILayout.BeginHorizontal();
+ EditorGUI.BeginDisabledGroup(true);
+ EditorGUILayout.TextField(title, variable);
+ EditorGUI.EndDisabledGroup();
+ if (GUILayout.Button("...", GUILayout.Width(30)))
+ {
+ string dummyPath = EditorUtility.OpenFolderPanel(title, variable, "");
+ if (string.IsNullOrEmpty(dummyPath))
+ return;
+
+ if (!dummyPath.Contains("Assets"))
+ {
+ Debug.LogWarning("New Path must be a folder within Assets!");
+ return;
+ }
+ variable = FileUtil.GetProjectRelativePath(dummyPath);
+ PlayerPrefs.SetString(playerpref, variable);
+ }
+ EditorGUILayout.EndHorizontal();
+ }
+
+ public static void RecreateFolders(string fullPath)
+ {
+ string[] folderNames = fullPath.Split('/');
+ string[] folderPaths = new string[folderNames.Length];
+ for (int i = 0; i < folderNames.Length; i++)
+ {
+ folderPaths[i] = folderNames[0];
+ for (int j = 1; j <= i; j++)
+ {
+ folderPaths[i] = folderPaths[i] + "/" + folderNames[j];
+ }
+ }
+ for (int i = 0; i < folderPaths.Length; i++)
+ {
+ if (!AssetDatabase.IsValidFolder(folderPaths[i]))
+ {
+ AssetDatabase.CreateFolder(folderPaths[i].Substring(0, folderPaths[i].LastIndexOf('/')), folderPaths[i].Substring(folderPaths[i].LastIndexOf('/') + 1, folderPaths[i].Length - folderPaths[i].LastIndexOf('/') - 1));
+ }
+
+ }
+ }
+ #endregion
+
+ private static void Credit()
+ {
+ GUIStyle creditLabelStyle = new GUIStyle(GUI.skin.label) { richText = true };
+ using (new GUILayout.HorizontalScope())
+ {
+ GUILayout.FlexibleSpace();
+ if (GUILayout.Button("<b>Made by Dreadrith#3238</b>",creditLabelStyle))
+ {
+ Application.OpenURL("https://github.com/Dreadrith/DreadScripts");
+ }
+
+ }
+ }
+ }
+
+ [System.Serializable]
+ public class ChannelTexture
+ {
+ public string name;
+ public Texture2D texture;
+ public bool invert;
+ public ColorMode mode = ColorMode.Red;
+ public enum ColorMode
+ {
+ Red,
+ Green,
+ Blue,
+ Alpha
+ }
+ public ChannelTexture(string n, int mode)
+ {
+ name = n;
+ SetMode(mode, true);
+ }
+
+ public void SetMode(int i, bool ignoreSave = false)
+ {
+ switch (i)
+ {
+ case 0:
+ mode = ColorMode.Red;
+ break;
+ case 1:
+ mode = ColorMode.Green;
+ break;
+ case 2:
+ mode = ColorMode.Blue;
+ break;
+ case 3:
+ mode = ColorMode.Alpha;
+ break;
+ }
+ if (!ignoreSave)
+ {
+ EditorPrefs.SetInt("TextureUtilityChannel" + name, i);
+ }
+ }
+
+ public Texture2D GetChannelColors(int width, int height, out float[] colors, bool unloadTempTexture)
+ {
+ if (!texture)
+ {
+ colors = null;
+ return null;
+ }
+ else
+ {
+ Texture2D newTexture = TextureUtility.GetColors(texture, width, height, out Color[] myColors, unloadTempTexture);
+ colors = myColors.Select(c =>
+ {
+ if (mode == ColorMode.Red)
+ return c.r;
+ if (mode == ColorMode.Green)
+ return c.g;
+ if (mode == ColorMode.Blue)
+ return c.b;
+
+ return c.a;
+ }).ToArray();
+ if (invert)
+ {
+ for (int i = 0; i < colors.Length; i++)
+ {
+ colors[i] = 1 - colors[i];
+ }
+ }
+ return newTexture;
+ }
+ }
+
+ public void DrawGUI()
+ {
+ GUIStyle buttonGroupStyle = new GUIStyle(GUI.skin.GetStyle("toolbarbutton")) { padding = new RectOffset(1, 1, 1, 1), margin = new RectOffset(0, 0, 1, 1) };
+ using (new GUILayout.VerticalScope("box"))
+ {
+ using (new GUILayout.HorizontalScope())
+ {
+ GUILayout.FlexibleSpace();
+ GUILayout.Label(name, "boldlabel");
+ GUILayout.FlexibleSpace();
+ }
+ using (new GUILayout.HorizontalScope())
+ {
+ GUILayout.FlexibleSpace();
+ bool dummy;
+ EditorGUI.BeginChangeCheck();
+ dummy = GUILayout.Toggle(mode == ColorMode.Red, "R", buttonGroupStyle, GUILayout.Width(16));
+ if (EditorGUI.EndChangeCheck())
+ if (dummy)
+ SetMode(0);
+
+ EditorGUI.BeginChangeCheck();
+ dummy = GUILayout.Toggle(mode == ColorMode.Green, "G", buttonGroupStyle, GUILayout.Width(16));
+ if (EditorGUI.EndChangeCheck())
+ if (dummy)
+ SetMode(1);
+
+ EditorGUI.BeginChangeCheck();
+ dummy = GUILayout.Toggle(mode == ColorMode.Blue, "B", buttonGroupStyle, GUILayout.Width(16));
+ if (EditorGUI.EndChangeCheck())
+ if (dummy)
+ SetMode(2);
+
+ EditorGUI.BeginChangeCheck();
+ dummy = GUILayout.Toggle(mode == ColorMode.Alpha, "A", buttonGroupStyle, GUILayout.Width(16));
+ if (EditorGUI.EndChangeCheck())
+ if (dummy)
+ SetMode(3);
+ GUILayout.FlexibleSpace();
+ }
+ using (new GUILayout.HorizontalScope())
+ {
+ GUILayout.FlexibleSpace();
+ texture = (Texture2D)EditorGUILayout.ObjectField("", texture, typeof(Texture2D), false, GUILayout.Width(66));
+ GUILayout.FlexibleSpace();
+ }
+ invert = GUILayout.Toggle(invert, "Invert", "toolbarbutton");
+ }
+ }
+
+
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/TextureUtility.cs.meta b/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/TextureUtility.cs.meta
new file mode 100644
index 00000000..8752585e
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/TextureUtility.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: cb8d94d5a72732e4b8b754b8138f95d8
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: