diff options
Diffstat (limited to 'VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools')
14 files changed, 1580 insertions, 0 deletions
diff --git a/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor.meta b/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor.meta new file mode 100644 index 00000000..9ff74d2b --- /dev/null +++ b/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 748053328138b574fb97df5308de5e24 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/BakeToVertexColorsEditor.cs b/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/BakeToVertexColorsEditor.cs new file mode 100644 index 00000000..cea014c1 --- /dev/null +++ b/VRCSDK3Worlds/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("Poi/Tools/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/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/BakeToVertexColorsEditor.cs.meta b/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/BakeToVertexColorsEditor.cs.meta new file mode 100644 index 00000000..d603b15a --- /dev/null +++ b/VRCSDK3Worlds/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/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/PoiData.cs b/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/PoiData.cs new file mode 100644 index 00000000..6c81040c --- /dev/null +++ b/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/PoiData.cs @@ -0,0 +1,50 @@ +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/"; + } + + internal static class PoiStyles + { + public static GUIStyle BigButton + { + get + { + if(_bigButton == null) + _bigButton= new GUIStyle("button") + { + fixedHeight = 18 * 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; + } +} diff --git a/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/PoiData.cs.meta b/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/PoiData.cs.meta new file mode 100644 index 00000000..2fae90dc --- /dev/null +++ b/VRCSDK3Worlds/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/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/PoiHelpers.cs b/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/PoiHelpers.cs new file mode 100644 index 00000000..787abb8b --- /dev/null +++ b/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/PoiHelpers.cs @@ -0,0 +1,394 @@ +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> + /// Removes suffix from the end of string then returns it + /// </summary> + /// <param name="str"></param> + /// <param name="suffixes">Each to be removed in order</param> + /// <returns></returns> + public static string RemoveSuffix(string str, string[] suffixes) + { + var suffixList = suffixes.ToList(); + suffixList.Remove(str); + + while(suffixList.Any(str.EndsWith)) + foreach(string sfx in suffixList) + { + string s = suffixSeparator + sfx; + if(!str.EndsWith(sfx)) + continue; + + int idx = str.LastIndexOf(s, StringComparison.Ordinal); + if(idx != -1) + str = str.Remove(idx, s.Length); + } + return str; + } + + /// <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 Vector2Int DrawResolutionPicker(Vector2Int size, ref bool linked, ref bool autoDetect, int[] presets = null, string[] presetNames = null) + { + EditorGUI.BeginDisabledGroup(autoDetect); + EditorGUILayout.BeginHorizontal(); + { + EditorGUILayout.PrefixLabel("Size"); + + EditorGUI.BeginChangeCheck(); + size.x = EditorGUILayout.IntField(size.x); + if(linked && EditorGUI.EndChangeCheck()) + size.y = size.x; + + EditorGUILayout.LabelField("x", GUILayout.MaxWidth(12)); + + EditorGUI.BeginChangeCheck(); + size.y = EditorGUILayout.IntField(size.y); + if(linked && EditorGUI.EndChangeCheck()) + size.x = size.y; + + if(presets != null && presetNames != null) + { + EditorGUI.BeginChangeCheck(); + int selectedPresetIndex = EditorGUILayout.Popup(GUIContent.none, -1, presetNames, GUILayout.MaxWidth(16)); + if(EditorGUI.EndChangeCheck() && selectedPresetIndex != -1) + size = new Vector2Int(presets[selectedPresetIndex], presets[selectedPresetIndex]); + } + + linked = GUILayout.Toggle(linked, "L", EditorStyles.miniButton, GUILayout.MaxWidth(16)); + } + EditorGUILayout.EndHorizontal(); + + EditorGUI.EndDisabledGroup(); + + autoDetect = EditorGUILayout.Toggle("Auto detect", autoDetect); + + return size; + } + + /// <summary> + /// Gets the combined maximum width and height of the passed in textures + /// </summary> + /// <param name="textures"></param> + /// <returns></returns> + internal static Vector2Int GetMaxSizeFromTextures(params Texture2D[] textures) + { + var sizes = textures.Where(tex => tex).Select(tex => new Vector2Int(tex.width, tex.height)).ToArray(); + if(sizes.Length == 0) + return default; + + int maxW = sizes.Max(wh => wh.x); + int maxH = sizes.Max(wh => wh.y); + return new Vector2Int(maxW, maxH); + } + + internal static Texture2D PackTextures(Vector2Int resolution, Texture2D red, Texture2D green, Texture2D blue, Texture2D alpha, bool invertRed, bool invertGreen, bool invertBlue, bool invertAlpha) + { + // Setup Material + var mat = new Material(PoiExtensions.PackerShader); + + mat.SetTexture("_Red", red); + mat.SetTexture("_Green", green); + mat.SetTexture("_Blue", blue); + mat.SetTexture("_Alpha", alpha); + + mat.SetInt("_Invert_Red", Convert.ToInt32(invertRed)); + mat.SetInt("_Invert_Green", Convert.ToInt32(invertGreen)); + mat.SetInt("_Invert_Blue", Convert.ToInt32(invertBlue)); + mat.SetInt("_Invert_Alpha", Convert.ToInt32(invertAlpha)); + + // Create texture and render to it + var tex = new Texture2D(resolution.x, resolution.y); + tex.BakeMaterialToTexture(mat); + + // Cleanup + DestroyAppropriate(mat); + + return tex; + } + + internal static Dictionary<string, Texture2D> UnpackTextureToChannels(Texture2D packedTexture, bool invert, Vector2Int resolution) + { + var channels = new Dictionary<string, Texture2D> + { + {PoiExtensions.PoiTextureChannel.Red.ToString().ToLower(), + new Texture2D(resolution.x, resolution.y, TextureFormat.RGB24, true)}, + {PoiExtensions.PoiTextureChannel.Green.ToString().ToLower(), + new Texture2D(resolution.x, resolution.y, TextureFormat.RGB24, true)}, + {PoiExtensions.PoiTextureChannel.Blue.ToString().ToLower(), + new Texture2D(resolution.x, resolution.y, TextureFormat.RGB24, true)}, + {PoiExtensions.PoiTextureChannel.Alpha.ToString().ToLower(), + new Texture2D(resolution.x, resolution.y, TextureFormat.RGB24, true)} + }; + + var mat = new Material(PoiExtensions.UnpackerShader); + mat.SetTexture("_MainTex", packedTexture); + mat.SetInt("_Invert", Convert.ToInt32(invert)); + + for(int i = 0; i < 4; i++) + { + mat.SetFloat("_Mode", i); + channels.ElementAt(i).Value.BakeMaterialToTexture(mat); + } + + return channels; + } + + internal static void DrawWithLabelWidth(float width, Action action) + { + if(action == null) + return; + float old = EditorGUIUtility.labelWidth; + action.Invoke(); + EditorGUIUtility.labelWidth = old; + } + + internal static PoiExtensions.PoiTextureChannel DrawChannelSelector(PoiExtensions.PoiTextureChannel currentSelection, params string[] labels) + { + if(labels == null) + return PoiExtensions.PoiTextureChannel.RGBA; + return (PoiExtensions.PoiTextureChannel)GUILayout.SelectionGrid((int)currentSelection, labels, labels.Length); + } + } + + internal static class PoiExtensions + { + public enum PoiTextureChannel { RGBA, Red, Green, Blue, Alpha } + public static Shader PackerShader + { + get + { + return Shader.Find("Hidden/Poi/TexturePacker"); + } + } + public static Shader UnpackerShader + { + get + { + return Shader.Find("Hidden/Poi/TextureUnpacker"); + } + } + + internal static Texture2D GetChannelAsTexture(this Texture2D tex, PoiTextureChannel chan, bool invert = false, Vector2Int sizeOverride = default) + { + if(chan == PoiTextureChannel.RGBA) + return tex; + + if(sizeOverride == default) + sizeOverride = new Vector2Int(tex.width, tex.height); + + Material mat = new Material(UnpackerShader); + mat.SetFloat("_Mode", (int)chan - 1); + mat.SetInt("_Invert", Convert.ToInt32(invert)); + mat.SetTexture("_MainTex", tex); + + var newTex = new Texture2D(sizeOverride.x, sizeOverride.y, TextureFormat.RGB24, true); + newTex.name = chan.ToString(); + newTex.BakeMaterialToTexture(mat); + newTex.Apply(false, false); + + return newTex; + } + + /// <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); + } + + internal static void SaveTextureAsset(this Texture2D tex, string assetPath, bool overwrite) + { + var bytes = tex.EncodeToPNG(); + + + // Ensure directory exists then convert path to local asset path + if(!assetPath.StartsWith("Assets", StringComparison.OrdinalIgnoreCase)) + { + Directory.CreateDirectory(Path.GetDirectoryName(assetPath)); + assetPath = PoiHelpers.AbsolutePathToLocalAssetsPath(assetPath); + } + else + { + string absolutePath = PoiHelpers.LocalAssetsPathToAbsolutePath(assetPath); + Directory.CreateDirectory(Path.GetDirectoryName(absolutePath)); + } + + if(AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(assetPath) && !overwrite) + assetPath = AssetDatabase.GenerateUniqueAssetPath(assetPath); + + File.WriteAllBytes(assetPath, bytes); + AssetDatabase.Refresh(); + } + + internal static Texture2D GetReadableTextureCopy(this Texture2D tex) + { + byte[] pix = tex.GetRawTextureData(); + Texture2D finalTex = new Texture2D(tex.width, tex.height, tex.format, false); + finalTex.LoadRawTextureData(pix); + finalTex.Apply(); + return finalTex; + } + + /// <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/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/PoiHelpers.cs.meta b/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/PoiHelpers.cs.meta new file mode 100644 index 00000000..5326296d --- /dev/null +++ b/VRCSDK3Worlds/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/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/TextureChannelPackerEditor.cs b/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/TextureChannelPackerEditor.cs new file mode 100644 index 00000000..dbc9517c --- /dev/null +++ b/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/TextureChannelPackerEditor.cs @@ -0,0 +1,373 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using UnityEditor; +using UnityEngine; +using TextureChannel = Poi.PoiExtensions.PoiTextureChannel; + +namespace Poi +{ + public class TextureChannelPackerEditor : EditorWindow + { + const string LOG_PREFIX = "<color=blue>Poi:</color> "; //color is hex or name + static readonly Vector2 MIN_WINDOW_SIZE = new Vector2(350, 500); + const int AUTO_SELECT_CEILING = 2048; + + const string INVERT_LABEL = "Invert"; + const string PACKED_TEXTURE_LABEL = "Texture"; + const string RED_TEXTURE_LABEL = "Red"; + const string GREEN_TEXTURE_LABEL = "Green"; + const string BLUE_TEXTURE_LABEL = "Blue"; + const string ALPHA_TEXTURE_LABEL = "Alpha"; + + float InvertToggleWidth + { + get + { + if(_invertLabelWidth == default) + _invertLabelWidth = EditorStyles.toggle.CalcSize(new GUIContent(INVERT_LABEL)).x; + return _invertLabelWidth; + } + } + + // Default values + string savePath = "Assets/_ChannelPacker"; + string packedName = "packed"; + string unpackedName = "unpacked"; + + // Version + Version version = new Version(1, 2); + string SubTitle + { + get + { + if(string.IsNullOrWhiteSpace(_subTitle)) + _subTitle = "by Pumkin - v" + version.ToString(); + return _subTitle; + } + } + + static EditorWindow Window + { + get + { + if(!_window) + _window = GetWindow<TextureChannelPackerEditor>(); + return _window; + } + } + + // Texture stuff + static int[] SizePresets { get; } = { 128, 256, 512, 1024, 2048, 4096 }; + string[] SizePresetNames + { + get + { + if(_sizeNames == null) + _sizeNames = SizePresets.Select(i => i + " x " + i).ToArray(); + return _sizeNames; + } + } + + Vector2Int PackSize { get; set; } = new Vector2Int(1024, 1024); + Vector2Int UnpackSize { get; set; } = new Vector2Int(1024, 1024); + + bool packSizeIsLinked = true; + bool unpackSizeIsLinked = true; + + bool packSizeAutoSelect = true; + bool unpackSizeAutoSelect = true; + + bool showChannelPicker = false; + + TextureChannel redTexChan, blueTexChan, greenTexChan, alphaTexChan, unpackChan; + + Texture2D packRed, packGreen, packBlue, packAlpha, unpackSource; + + bool redInvert, greenInvert, blueInvert, alphaInvert, unpackInvert; + + string[] ChannelLabels { get; } = { "All", "Red", "Green", "Blue", "Alpha" }; + + bool PackerShadersExist + { + get + { + bool everythingIsAlwaysFine = true; + + if(!PoiExtensions.UnpackerShader) + { + Debug.LogWarning(LOG_PREFIX + "Unpacker shader is missing or invalid. Can't unpack textures."); + everythingIsAlwaysFine = false; + } + + if(!PoiExtensions.PackerShader) + { + Debug.LogWarning(LOG_PREFIX + "Packer shader is missing or invalid. Can't pack textures."); + everythingIsAlwaysFine = false; + } + + return everythingIsAlwaysFine; + } + } + + // UI + enum Tab { Pack, Unpack } + int selectedTab = 0; + string[] TabNames + { + get + { + if(_tabNames == null) + _tabNames = Enum.GetNames(typeof(Tab)); + return _tabNames; + } + } + + + [MenuItem("Poi/Tools/Texture Packer", priority = 0)] + public static void ShowWindow() + { + //Show existing window instance. If one doesn't exist, make one. + Window.autoRepaintOnSceneChange = true; + Window.minSize = MIN_WINDOW_SIZE; + + Window.Show(); + Window.titleContent = new GUIContent("Texture Packer"); + } + + #region Drawing GUI + + void OnGUI() + { + EditorGUILayout.LabelField("Poi Texture Packer", PoiStyles.TitleLabel); + EditorGUILayout.LabelField(SubTitle); + + PoiHelpers.DrawLine(); + + selectedTab = GUILayout.Toolbar(selectedTab, TabNames); + + if(selectedTab == (int)Tab.Pack) + DrawPackUI(); + else + DrawUnpackUI(); + } + + void DrawPackUI() + { + _scroll = EditorGUILayout.BeginScrollView(_scroll); + { + EditorGUI.BeginChangeCheck(); + { + DrawTextureSelector(RED_TEXTURE_LABEL, ref packRed, ref redTexChan, ref redInvert); + DrawTextureSelector(GREEN_TEXTURE_LABEL, ref packGreen, ref greenTexChan, ref greenInvert); + DrawTextureSelector(BLUE_TEXTURE_LABEL, ref packBlue, ref blueTexChan, ref blueInvert); + DrawTextureSelector(ALPHA_TEXTURE_LABEL, ref packAlpha, ref alphaTexChan, ref alphaInvert); + } + if(EditorGUI.EndChangeCheck() && packSizeAutoSelect) + { + // Get biggest texture size from selections and make a selection in our sizes list + var tempSize = PoiHelpers.GetMaxSizeFromTextures(packRed, packGreen, packBlue, packAlpha); + if(tempSize != default) + PackSize = tempSize.ClosestPowerOfTwo(AUTO_SELECT_CEILING); + } + } + EditorGUILayout.EndScrollView(); + + DrawShowChannelPicker(ref showChannelPicker); + + bool disabled = new bool[] { packRed, packGreen, packBlue, packAlpha }.Count(b => b) < 2; + EditorGUI.BeginDisabledGroup(disabled); + { + PackSize = DrawTextureSizeSettings(PackSize, ref packedName, ref packSizeIsLinked, ref packSizeAutoSelect); + + if(GUILayout.Button("Pack", PoiStyles.BigButton)) + DoPack(); + + EditorGUILayout.Space(); + } + EditorGUI.EndDisabledGroup(); + } + + private void DrawShowChannelPicker(ref bool pickerValue) + { + EditorGUILayout.BeginHorizontal(EditorStyles.helpBox); + pickerValue = EditorGUILayout.ToggleLeft("Pick source channel", pickerValue); + EditorGUILayout.EndHorizontal(); + } + + void DrawUnpackUI() + { + _scroll = EditorGUILayout.BeginScrollView(_scroll); + { + EditorGUI.BeginChangeCheck(); + { + DrawTextureSelector(PACKED_TEXTURE_LABEL, ref unpackSource, ref unpackChan, ref unpackInvert); + } + if(EditorGUI.EndChangeCheck() && unpackSizeAutoSelect) + { + // Get biggest texture size from selections and make a selection in our sizes list + var tempSize = PoiHelpers.GetMaxSizeFromTextures(unpackSource); + if(tempSize != default) + UnpackSize = tempSize.ClosestPowerOfTwo(AUTO_SELECT_CEILING); + } + + DrawShowChannelPicker(ref showChannelPicker); + } + EditorGUILayout.EndScrollView(); + + EditorGUI.BeginDisabledGroup(!unpackSource); + { + UnpackSize = DrawTextureSizeSettings(UnpackSize, ref unpackedName, ref unpackSizeIsLinked, ref unpackSizeAutoSelect); + + if(GUILayout.Button("Unpack", PoiStyles.BigButton)) + DoUnpack(unpackChan); + } + EditorGUI.EndDisabledGroup(); + + EditorGUILayout.Space(); + } + + #endregion + + #region Packing and Unpacking + + void DoPack() + { + if(!PackerShadersExist) + return; + + Texture2D red = packRed; + Texture2D green = packGreen; + Texture2D blue = packBlue; + Texture2D alpha = packAlpha; + + if(showChannelPicker) + { + red = packRed.GetChannelAsTexture(redTexChan, unpackInvert); + green = packGreen.GetChannelAsTexture(greenTexChan, unpackInvert); + blue = packBlue.GetChannelAsTexture(blueTexChan, unpackInvert); + alpha = packAlpha.GetChannelAsTexture(alphaTexChan, unpackInvert); + } + + Texture2D packResult = PoiHelpers.PackTextures(PackSize, red, green, blue, alpha, redInvert, greenInvert, blueInvert, alphaInvert); + if(packResult) + { + string path = $"{savePath}/Packed/{packedName}.png"; + packResult.SaveTextureAsset(path, true); + Debug.Log(LOG_PREFIX + "Finished packing texture at " + path); + PoiHelpers.PingAssetAtPath(path); + } + + } + + void DoUnpack(TextureChannel singleChannel = TextureChannel.RGBA) + { + if(!PackerShadersExist) + return; + + var channelTextures = new Dictionary<string, Texture2D>(); + if(singleChannel == TextureChannel.RGBA) + channelTextures = PoiHelpers.UnpackTextureToChannels(unpackSource, unpackInvert, UnpackSize); + else + channelTextures[singleChannel.ToString().ToLower()] = unpackSource.GetChannelAsTexture(singleChannel, unpackInvert, UnpackSize); + + + string pingPath = null; + pingPath = SaveTextures(channelTextures, pingPath); + + Debug.Log(LOG_PREFIX + "Finished unpacking texture at " + pingPath); + PoiHelpers.PingAssetAtPath(pingPath); + } + + #endregion + + #region Helpers + + string SaveTextures(Dictionary<string, Texture2D> output, string pingPath) + { + try + { + AssetDatabase.StartAssetEditing(); + foreach(var kv in output) + { + if(string.IsNullOrWhiteSpace(pingPath)) + pingPath = $"{savePath}/Unpacked/{unpackedName}_{kv.Key}.png"; + kv.Value?.SaveTextureAsset($"{savePath}/Unpacked/{unpackedName}_{kv.Key}.png", true); + } + } + catch { } + finally + { + AssetDatabase.StopAssetEditing(); + } + + return pingPath; + } + + void DrawTextureSelector(string label, ref Texture2D tex) + { + EditorGUILayout.BeginVertical(EditorStyles.helpBox); + { + tex = EditorGUILayout.ObjectField(label, tex, typeof(Texture2D), true, GUILayout.ExpandHeight(true)) as Texture2D; + } + EditorGUILayout.EndHorizontal(); + } + + void DrawTextureSelector(string label, ref Texture2D tex, ref TextureChannel selectedChannel, ref bool invert) + { + EditorGUILayout.BeginVertical(EditorStyles.helpBox); + { + EditorGUILayout.BeginHorizontal(); + { + EditorGUILayout.BeginVertical(); + { + var labelContent = new GUIContent(label); + var size = EditorStyles.boldLabel.CalcSize(labelContent); + + EditorGUILayout.LabelField(labelContent, EditorStyles.boldLabel, GUILayout.MaxWidth(size.x)); + + GUILayout.Space(15 * EditorGUIUtility.pixelsPerPoint); + + GUILayout.FlexibleSpace(); + + invert = EditorGUILayout.ToggleLeft(INVERT_LABEL, invert, GUILayout.MaxWidth(InvertToggleWidth)); + } + EditorGUILayout.EndVertical(); + + tex = EditorGUILayout.ObjectField(GUIContent.none, tex, typeof(Texture2D), true, GUILayout.ExpandHeight(true)) as Texture2D; + } + EditorGUILayout.EndHorizontal(); + + if(showChannelPicker) + { + EditorGUI.BeginDisabledGroup(!tex); + selectedChannel = PoiHelpers.DrawChannelSelector(selectedChannel, ChannelLabels); + EditorGUI.EndDisabledGroup(); + } + } + EditorGUILayout.EndVertical(); + } + + Vector2Int DrawTextureSizeSettings(Vector2Int size, ref string fileName, ref bool sizeIsLinked, ref bool sizeAutoSelect) + { + EditorGUILayout.BeginVertical(EditorStyles.helpBox); + { + fileName = EditorGUILayout.TextField("File name", fileName); + EditorGUILayout.Space(); + size = PoiHelpers.DrawResolutionPicker(size, ref sizeIsLinked, ref sizeAutoSelect, SizePresets, SizePresetNames); + } + EditorGUILayout.EndVertical(); + return size; + } + + #endregion + + string[] _tabNames; + string[] _sizeNames; + static EditorWindow _window; + string _subTitle; + Vector2 _scroll; + float _invertLabelWidth; + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/TextureChannelPackerEditor.cs.meta b/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/TextureChannelPackerEditor.cs.meta new file mode 100644 index 00000000..49e700e9 --- /dev/null +++ b/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Editor/TextureChannelPackerEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1e29ffa815f2cd648839d9b094a4631f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Resources.meta b/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Resources.meta new file mode 100644 index 00000000..604e4ea7 --- /dev/null +++ b/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 362826441ef464c458314d76942a2c67 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Resources/PoiTexturePacker.shader b/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Resources/PoiTexturePacker.shader new file mode 100644 index 00000000..2f366a9e --- /dev/null +++ b/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Resources/PoiTexturePacker.shader @@ -0,0 +1,169 @@ +// Made with Amplify Shader Editor +// Available at the Unity Asset Store - http://u3d.as/y3X +Shader "Hidden/Poi/TexturePacker" +{ + Properties + { + _Invert_Red("Invert_Red", Float) = 0 + _Invert_Green("Invert_Green", Float) = 0 + _Invert_Blue("Invert_Blue", Float) = 0 + _Invert_Alpha("Invert_Alpha", Float) = 0 + _Red("Red", 2D) = "white" {} + _Green("Green", 2D) = "white" {} + _Blue("Blue", 2D) = "white" {} + _Alpha("Alpha", 2D) = "white" {} + [HideInInspector] _texcoord( "", 2D ) = "white" {} + } + + SubShader + { + Tags { "RenderType"="Opaque" } + LOD 100 + CGINCLUDE + #pragma target 3.0 + ENDCG + Blend Off + Cull Back + ColorMask RGBA + ZWrite On + ZTest LEqual + Offset 0 , 0 + + + + Pass + { + Name "Unlit" + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #pragma multi_compile_instancing + #include "UnityCG.cginc" + + + struct appdata + { + float4 vertex : POSITION; + UNITY_VERTEX_INPUT_INSTANCE_ID + float4 ase_texcoord : TEXCOORD0; + }; + + struct v2f + { + float4 vertex : SV_POSITION; + float4 ase_texcoord : TEXCOORD0; + UNITY_VERTEX_OUTPUT_STEREO + UNITY_VERTEX_INPUT_INSTANCE_ID + }; + + uniform sampler2D _Red; + uniform float4 _Red_ST; + uniform float _Invert_Red; + uniform sampler2D _Green; + uniform float4 _Green_ST; + uniform float _Invert_Green; + uniform sampler2D _Blue; + uniform float4 _Blue_ST; + uniform float _Invert_Blue; + uniform sampler2D _Alpha; + uniform float4 _Alpha_ST; + uniform float _Invert_Alpha; + + v2f vert ( appdata v ) + { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + UNITY_TRANSFER_INSTANCE_ID(v, o); + + o.ase_texcoord.xy = v.ase_texcoord.xy; + + //setting value to unused interpolator channels and avoid initialization warnings + o.ase_texcoord.zw = 0; + + v.vertex.xyz += float3(0,0,0) ; + o.vertex = UnityObjectToClipPos(v.vertex); + return o; + } + + fixed4 frag (v2f i ) : SV_Target + { + UNITY_SETUP_INSTANCE_ID(i); + fixed4 finalColor; + float2 uv_Red = i.ase_texcoord.xy * _Red_ST.xy + _Red_ST.zw; + float4 tex2DNode28 = tex2D( _Red, uv_Red ); + float4 temp_cast_0 = (_Invert_Red).xxxx; + float4 lerpResult27 = lerp( tex2DNode28 , ( temp_cast_0 - tex2DNode28 ) , _Invert_Red); + float2 uv_Green = i.ase_texcoord.xy * _Green_ST.xy + _Green_ST.zw; + float4 tex2DNode12 = tex2D( _Green, uv_Green ); + float4 temp_cast_2 = (_Invert_Green).xxxx; + float4 lerpResult20 = lerp( tex2DNode12 , ( temp_cast_2 - tex2DNode12 ) , _Invert_Green); + float2 uv_Blue = i.ase_texcoord.xy * _Blue_ST.xy + _Blue_ST.zw; + float4 tex2DNode14 = tex2D( _Blue, uv_Blue ); + float4 temp_cast_4 = (_Invert_Blue).xxxx; + float4 lerpResult21 = lerp( tex2DNode14 , ( temp_cast_4 - tex2DNode14 ) , _Invert_Blue); + float2 uv_Alpha = i.ase_texcoord.xy * _Alpha_ST.xy + _Alpha_ST.zw; + float4 tex2DNode13 = tex2D( _Alpha, uv_Alpha ); + float4 temp_cast_6 = (_Invert_Alpha).xxxx; + float4 lerpResult19 = lerp( tex2DNode13 , ( temp_cast_6 - tex2DNode13 ) , _Invert_Alpha); + float4 appendResult30 = (float4(lerpResult27.r , lerpResult20.r , lerpResult21.r , lerpResult19.r)); + + + finalColor = appendResult30; + return finalColor; + } + ENDCG + } + } + CustomEditor "ASEMaterialInspector" + + +} +/*ASEBEGIN +Version=15902 +0;0;1368;850;1368.399;595.2781;1;True;False +Node;AmplifyShaderEditor.SamplerNode;14;-1193.289,314.7757;Float;True;Property;_Blue;Blue;6;0;Create;True;0;0;False;0;None;None;True;0;False;white;Auto;False;Object;-1;Auto;Texture2D;6;0;SAMPLER2D;;False;1;FLOAT2;0,0;False;2;FLOAT;0;False;3;FLOAT2;0,0;False;4;FLOAT2;0,0;False;5;FLOAT;1;False;5;COLOR;0;FLOAT;1;FLOAT;2;FLOAT;3;FLOAT;4 +Node;AmplifyShaderEditor.RangedFloatNode;25;-815.7044,759.9294;Float;False;Property;_Invert_Alpha;Invert_Alpha;3;0;Create;True;0;0;False;0;0;0;0;0;0;1;FLOAT;0 +Node;AmplifyShaderEditor.RangedFloatNode;15;-819.5868,472.4816;Float;False;Property;_Invert_Blue;Invert_Blue;2;0;Create;True;0;0;False;0;0;0;0;0;0;1;FLOAT;0 +Node;AmplifyShaderEditor.RangedFloatNode;31;-803.4256,177.2413;Float;False;Property;_Invert_Green;Invert_Green;1;0;Create;True;0;0;False;0;0;0;0;0;0;1;FLOAT;0 +Node;AmplifyShaderEditor.RangedFloatNode;29;-795.8423,-109.6157;Float;False;Property;_Invert_Red;Invert_Red;0;0;Create;True;0;0;False;0;0;0;0;0;0;1;FLOAT;0 +Node;AmplifyShaderEditor.SamplerNode;28;-1189.017,-285.634;Float;True;Property;_Red;Red;4;0;Create;True;0;0;False;0;None;None;True;0;False;white;Auto;False;Object;-1;Auto;Texture2D;6;0;SAMPLER2D;;False;1;FLOAT2;0,0;False;2;FLOAT;0;False;3;FLOAT2;0,0;False;4;FLOAT2;0,0;False;5;FLOAT;1;False;5;COLOR;0;FLOAT;1;FLOAT;2;FLOAT;3;FLOAT;4 +Node;AmplifyShaderEditor.SamplerNode;12;-1199.358,5.317238;Float;True;Property;_Green;Green;5;0;Create;True;0;0;False;0;None;None;True;0;False;white;Auto;False;Object;-1;Auto;Texture2D;6;0;SAMPLER2D;;False;1;FLOAT2;0,0;False;2;FLOAT;0;False;3;FLOAT2;0,0;False;4;FLOAT2;0,0;False;5;FLOAT;1;False;5;COLOR;0;FLOAT;1;FLOAT;2;FLOAT;3;FLOAT;4 +Node;AmplifyShaderEditor.SamplerNode;13;-1182.523,665.4475;Float;True;Property;_Alpha;Alpha;7;0;Create;True;0;0;False;0;None;None;True;0;False;white;Auto;False;Object;-1;Auto;Texture2D;6;0;SAMPLER2D;;False;1;FLOAT2;0,0;False;2;FLOAT;0;False;3;FLOAT2;0,0;False;4;FLOAT2;0,0;False;5;FLOAT;1;False;5;COLOR;0;FLOAT;1;FLOAT;2;FLOAT;3;FLOAT;4 +Node;AmplifyShaderEditor.SimpleSubtractOpNode;16;-610.2974,-218.5994;Float;False;2;0;FLOAT;0;False;1;COLOR;0,0,0,0;False;1;COLOR;0 +Node;AmplifyShaderEditor.SimpleSubtractOpNode;26;-570.7031,710.9296;Float;False;2;0;FLOAT;0;False;1;COLOR;0,0,0,0;False;1;COLOR;0 +Node;AmplifyShaderEditor.SimpleSubtractOpNode;17;-612.9231,67.14128;Float;False;2;0;FLOAT;0;False;1;COLOR;0,0,0,0;False;1;COLOR;0 +Node;AmplifyShaderEditor.SimpleSubtractOpNode;18;-589.0041,392.5837;Float;False;2;0;FLOAT;0;False;1;COLOR;0,0,0,0;False;1;COLOR;0 +Node;AmplifyShaderEditor.LerpOp;19;-279.5903,619.9736;Float;False;3;0;COLOR;0,0,0,0;False;1;COLOR;0,0,0,0;False;2;FLOAT;0;False;1;COLOR;0 +Node;AmplifyShaderEditor.LerpOp;27;-318.2486,-275.2707;Float;False;3;0;COLOR;0,0,0,0;False;1;COLOR;0,0,0,0;False;2;FLOAT;0;False;1;COLOR;0 +Node;AmplifyShaderEditor.LerpOp;20;-299.71,16.80488;Float;False;3;0;COLOR;0,0,0,0;False;1;COLOR;0,0,0,0;False;2;FLOAT;0;False;1;COLOR;0 +Node;AmplifyShaderEditor.LerpOp;21;-296.069,300.6409;Float;False;3;0;COLOR;0,0,0,0;False;1;COLOR;0,0,0,0;False;2;FLOAT;0;False;1;COLOR;0 +Node;AmplifyShaderEditor.DynamicAppendNode;30;98.28339,102.1202;Float;False;FLOAT4;4;0;FLOAT;0;False;1;FLOAT;0;False;2;FLOAT;0;False;3;FLOAT;0;False;1;FLOAT4;0 +Node;AmplifyShaderEditor.TemplateMultiPassMasterNode;0;369.802,98.57185;Float;False;True;2;Float;ASEMaterialInspector;0;1;Hidden/Poi/TexturePacker;0770190933193b94aaa3065e307002fa;0;0;Unlit;2;True;0;1;False;-1;0;False;-1;0;1;False;-1;0;False;-1;True;0;False;-1;0;False;-1;True;0;False;-1;True;True;True;True;True;0;False;-1;True;False;255;False;-1;255;False;-1;255;False;-1;7;False;-1;1;False;-1;1;False;-1;1;False;-1;7;False;-1;1;False;-1;1;False;-1;1;False;-1;True;1;False;-1;True;3;False;-1;True;True;0;False;-1;0;False;-1;True;1;RenderType=Opaque=RenderType;True;2;0;False;False;False;False;False;False;False;False;False;False;0;;0;0;Standard;0;2;0;FLOAT4;0,0,0,0;False;1;FLOAT3;0,0,0;False;0 +WireConnection;16;0;29;0 +WireConnection;16;1;28;0 +WireConnection;26;0;25;0 +WireConnection;26;1;13;0 +WireConnection;17;0;31;0 +WireConnection;17;1;12;0 +WireConnection;18;0;15;0 +WireConnection;18;1;14;0 +WireConnection;19;0;13;0 +WireConnection;19;1;26;0 +WireConnection;19;2;25;0 +WireConnection;27;0;28;0 +WireConnection;27;1;16;0 +WireConnection;27;2;29;0 +WireConnection;20;0;12;0 +WireConnection;20;1;17;0 +WireConnection;20;2;31;0 +WireConnection;21;0;14;0 +WireConnection;21;1;18;0 +WireConnection;21;2;15;0 +WireConnection;30;0;27;0 +WireConnection;30;1;20;0 +WireConnection;30;2;21;0 +WireConnection;30;3;19;0 +WireConnection;0;0;30;0 +ASEEND*/ +//CHKSM=2C30DB01285F07958B9316BD81CB0A64AD7E3B0E
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Resources/PoiTexturePacker.shader.meta b/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Resources/PoiTexturePacker.shader.meta new file mode 100644 index 00000000..1658d888 --- /dev/null +++ b/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Resources/PoiTexturePacker.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 71129bd3774e04d48827a25fc98d45a7 +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Resources/PoiTextureUnpacker.shader b/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Resources/PoiTextureUnpacker.shader new file mode 100644 index 00000000..736465be --- /dev/null +++ b/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Resources/PoiTextureUnpacker.shader @@ -0,0 +1,156 @@ +// Made with Amplify Shader Editor +// Available at the Unity Asset Store - http://u3d.as/y3X +Shader "Hidden/Poi/TextureUnpacker" +{ + Properties + { + _MainTex("MainTex", 2D) = "white" {} + _Mode("Mode", Range( 0 , 3)) = 3 + _Invert("Invert", Float) = 0 + [HideInInspector] _texcoord( "", 2D ) = "white" {} + } + + SubShader + { + Tags { "RenderType"="Opaque" } + LOD 100 + CGINCLUDE + #pragma target 3.0 + ENDCG + Blend Off + Cull Back + ColorMask RGBA + ZWrite On + ZTest LEqual + Offset 0 , 0 + + + + Pass + { + Name "Unlit" + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #pragma multi_compile_instancing + #include "UnityCG.cginc" + + + struct appdata + { + float4 vertex : POSITION; + UNITY_VERTEX_INPUT_INSTANCE_ID + float4 ase_texcoord : TEXCOORD0; + }; + + struct v2f + { + float4 vertex : SV_POSITION; + float4 ase_texcoord : TEXCOORD0; + UNITY_VERTEX_OUTPUT_STEREO + UNITY_VERTEX_INPUT_INSTANCE_ID + }; + + uniform float _Mode; + uniform sampler2D _MainTex; + uniform float4 _MainTex_ST; + uniform float _Invert; + + v2f vert ( appdata v ) + { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + UNITY_TRANSFER_INSTANCE_ID(v, o); + + o.ase_texcoord.xy = v.ase_texcoord.xy; + + //setting value to unused interpolator channels and avoid initialization warnings + o.ase_texcoord.zw = 0; + + v.vertex.xyz += float3(0,0,0) ; + o.vertex = UnityObjectToClipPos(v.vertex); + return o; + } + + fixed4 frag (v2f i ) : SV_Target + { + UNITY_SETUP_INSTANCE_ID(i); + fixed4 finalColor; + float2 uv_MainTex = i.ase_texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw; + float4 tex2DNode32 = tex2D( _MainTex, uv_MainTex ); + float ifLocalVar34 = 0; + if( _Mode == 0.0 ) + ifLocalVar34 = tex2DNode32.r; + float ifLocalVar35 = 0; + if( _Mode == 1.0 ) + ifLocalVar35 = tex2DNode32.g; + float ifLocalVar36 = 0; + if( _Mode == 2.0 ) + ifLocalVar36 = tex2DNode32.b; + float ifLocalVar37 = 0; + if( _Mode == 3.0 ) + ifLocalVar37 = tex2DNode32.a; + float4 ifLocalVar42 = 0; + if( _Mode < 0.0 ) + ifLocalVar42 = tex2DNode32; + float4 ifLocalVar43 = 0; + if( _Mode > 3.0 ) + ifLocalVar43 = tex2DNode32; + float4 temp_output_40_0 = ( ifLocalVar34 + ifLocalVar35 + ifLocalVar36 + ifLocalVar37 + ifLocalVar42 + ifLocalVar43 ); + float4 temp_cast_0 = (_Invert).xxxx; + float4 lerpResult46 = lerp( temp_output_40_0 , ( temp_cast_0 - temp_output_40_0 ) , _Invert); + + + finalColor = lerpResult46; + return finalColor; + } + ENDCG + } + } + CustomEditor "ASEMaterialInspector" + + +} +/*ASEBEGIN +Version=15902 +0;0;1368;850;930.0129;673.0209;1.753676;True;False +Node;AmplifyShaderEditor.SamplerNode;32;-446.011,1.547681;Float;True;Property;_MainTex;MainTex;0;0;Create;True;0;0;False;0;None;None;True;0;False;white;Auto;False;Object;-1;Auto;Texture2D;6;0;SAMPLER2D;;False;1;FLOAT2;0,0;False;2;FLOAT;0;False;3;FLOAT2;0,0;False;4;FLOAT2;0,0;False;5;FLOAT;1;False;5;COLOR;0;FLOAT;1;FLOAT;2;FLOAT;3;FLOAT;4 +Node;AmplifyShaderEditor.RangedFloatNode;33;-414.2798,-86.22936;Float;False;Property;_Mode;Mode;1;0;Create;True;0;0;False;0;3;0;0;3;0;1;FLOAT;0 +Node;AmplifyShaderEditor.ConditionalIfNode;35;17.04439,123.3313;Float;False;False;5;0;FLOAT;0;False;1;FLOAT;1;False;2;FLOAT;0;False;3;FLOAT;0;False;4;FLOAT;0;False;1;FLOAT;0 +Node;AmplifyShaderEditor.ConditionalIfNode;36;17.16646,287.5046;Float;False;False;5;0;FLOAT;0;False;1;FLOAT;2;False;2;FLOAT;0;False;3;FLOAT;0;False;4;FLOAT;0;False;1;FLOAT;0 +Node;AmplifyShaderEditor.ConditionalIfNode;37;15.75801,456.534;Float;False;False;5;0;FLOAT;0;False;1;FLOAT;3;False;2;FLOAT;0;False;3;FLOAT;0;False;4;FLOAT;0;False;1;FLOAT;0 +Node;AmplifyShaderEditor.ConditionalIfNode;34;17.15299,-44.90865;Float;False;False;5;0;FLOAT;0;False;1;FLOAT;0;False;2;FLOAT;0;False;3;FLOAT;0;False;4;FLOAT;0;False;1;FLOAT;0 +Node;AmplifyShaderEditor.ConditionalIfNode;42;19.07808,-276.4948;Float;False;False;5;0;FLOAT;0;False;1;FLOAT;0;False;2;FLOAT;0;False;3;FLOAT;0;False;4;COLOR;0,0,0,0;False;1;COLOR;0 +Node;AmplifyShaderEditor.ConditionalIfNode;43;19.07608,697.2275;Float;False;False;5;0;FLOAT;0;False;1;FLOAT;3;False;2;COLOR;0,0,0,0;False;3;FLOAT;0;False;4;COLOR;0,0,0,0;False;1;COLOR;0 +Node;AmplifyShaderEditor.SimpleAddOpNode;40;370.6085,1.924235;Float;True;6;6;0;FLOAT;0;False;1;FLOAT;0;False;2;FLOAT;0;False;3;FLOAT;0;False;4;COLOR;0,0,0,0;False;5;COLOR;0,0,0,0;False;1;COLOR;0 +Node;AmplifyShaderEditor.RangedFloatNode;44;440.5474,232.555;Float;False;Property;_Invert;Invert;2;0;Create;True;0;0;False;0;0;0;0;0;0;1;FLOAT;0 +Node;AmplifyShaderEditor.SimpleSubtractOpNode;45;609.8499,63.25506;Float;False;2;0;FLOAT;0;False;1;COLOR;0,0,0,0;False;1;COLOR;0 +Node;AmplifyShaderEditor.LerpOp;46;780.463,-0.6814048;Float;False;3;0;COLOR;0,0,0,0;False;1;COLOR;0,0,0,0;False;2;FLOAT;0;False;1;COLOR;0 +Node;AmplifyShaderEditor.TemplateMultiPassMasterNode;0;975.2405,-1.718735;Float;False;True;2;Float;ASEMaterialInspector;0;1;Hidden/Poi/TextureUnpacker;0770190933193b94aaa3065e307002fa;0;0;Unlit;2;True;0;1;False;-1;0;False;-1;0;1;False;-1;0;False;-1;True;0;False;-1;0;False;-1;True;0;False;-1;True;True;True;True;True;0;False;-1;True;False;255;False;-1;255;False;-1;255;False;-1;7;False;-1;1;False;-1;1;False;-1;1;False;-1;7;False;-1;1;False;-1;1;False;-1;1;False;-1;True;1;False;-1;True;3;False;-1;True;True;0;False;-1;0;False;-1;True;1;RenderType=Opaque=RenderType;True;2;0;False;False;False;False;False;False;False;False;False;False;0;;0;0;Standard;0;2;0;FLOAT4;0,0,0,0;False;1;FLOAT3;0,0,0;False;0 +WireConnection;35;0;33;0 +WireConnection;35;3;32;2 +WireConnection;36;0;33;0 +WireConnection;36;3;32;3 +WireConnection;37;0;33;0 +WireConnection;37;3;32;4 +WireConnection;34;0;33;0 +WireConnection;34;3;32;1 +WireConnection;42;0;33;0 +WireConnection;42;4;32;0 +WireConnection;43;0;33;0 +WireConnection;43;2;32;0 +WireConnection;40;0;34;0 +WireConnection;40;1;35;0 +WireConnection;40;2;36;0 +WireConnection;40;3;37;0 +WireConnection;40;4;42;0 +WireConnection;40;5;43;0 +WireConnection;45;0;44;0 +WireConnection;45;1;40;0 +WireConnection;46;0;40;0 +WireConnection;46;1;45;0 +WireConnection;46;2;44;0 +WireConnection;0;0;46;0 +ASEEND*/ +//CHKSM=FB476DC839C9D986CDFBE64BF68940FC3E2666AE
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Resources/PoiTextureUnpacker.shader.meta b/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Resources/PoiTextureUnpacker.shader.meta new file mode 100644 index 00000000..d7c2c86d --- /dev/null +++ b/VRCSDK3Worlds/Assets/_PoiyomiShaders/Scripts/poi-tools/Resources/PoiTextureUnpacker.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 245e67c21ccaa9a43ad7e84d1c7bb5fc +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: |