summaryrefslogtreecommitdiff
path: root/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor
diff options
context:
space:
mode:
Diffstat (limited to 'VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor')
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor.meta8
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Config.cs191
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Config.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/DataStructs.cs630
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/DataStructs.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Drawers.cs1501
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Drawers.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/EditorChanger.cs166
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/EditorChanger.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/EditorStructs.cs943
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/EditorStructs.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/GUIHelper.cs1049
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/GUIHelper.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/GradiantEditor.cs258
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/GradiantEditor.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Helper.cs1679
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Helper.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/HelperWeb.cs227
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/HelperWeb.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Locale.cs162
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Locale.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/MaterialLinker.cs260
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/MaterialLinker.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Mediator.cs25
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Mediator.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ModuleHandler.cs367
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ModuleHandler.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Parser.cs408
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Parser.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Presets.cs360
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Presets.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Settings.cs422
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Settings.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ShaderOptimizer.cs2110
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ShaderOptimizer.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ShaderTranslator.cs238
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ShaderTranslator.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Styles.cs92
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Styles.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryAssemblyDefinition.asmdef14
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryAssemblyDefinition.asmdef.meta7
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryEditor.cs726
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryEditor.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryFileBuilder.cs65
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryFileBuilder.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryTexturePacker.compute77
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryTexturePacker.compute.meta8
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Unity.cs245
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Unity.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/UploadAnchorOverrideSetter.cs102
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/UploadAnchorOverrideSetter.cs.meta11
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples.meta8
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/Example1.shader440
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/Example1.shader.meta8
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/Example2.shader435
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/Example2.shader.meta8
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/ThryLabelExample.txt6
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/ThryLabelExample.txt.meta7
-rwxr-xr-xVRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/ThryPresetsExample.txt0
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/ThryPresetsExample.txt.meta7
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/thry_locale_example.csv3
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/thry_locale_example.csv.meta7
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/LICENSE.md674
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/LICENSE.md.meta7
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/README.md4
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/README.md.meta7
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Resources.meta8
-rwxr-xr-xVRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Resources/thryEditor_iconThry.pngbin0 -> 71731 bytes
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Resources/thryEditor_iconThry.png.meta116
-rwxr-xr-xVRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Resources/thryEditor_link.pngbin0 -> 262 bytes
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Resources/thryEditor_link.png.meta104
-rwxr-xr-xVRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Resources/thry_rect.pngbin0 -> 9459 bytes
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Resources/thry_rect.png.meta116
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/docs.html639
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/docs.html.meta7
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/thry_editor_locale.csv63
-rw-r--r--VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/thry_editor_locale.csv.meta7
77 files changed, 15274 insertions, 0 deletions
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor.meta
new file mode 100644
index 00000000..9fc152bd
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 009d7c02d58e9e946aa96449dac24a84
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Config.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Config.cs
new file mode 100644
index 00000000..a21835c1
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Config.cs
@@ -0,0 +1,191 @@
+// Material/Shader Inspector for Unity 2017/2018
+// Copyright (C) 2019 Thryrallo
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Globalization;
+using System.IO;
+using UnityEditor;
+using UnityEngine;
+
+namespace Thry
+{
+ public class Config
+ {
+ // consts
+ private const string PATH_CONFIG_FILE = "Thry/Config.json";
+ private const string VERSION = "2.25.0";
+
+ // static
+ private static Config config;
+
+ public static void OnCompile()
+ {
+ if (!File.Exists(PATH_CONFIG_FILE))
+ {
+ //Settings.firstTimePopup();
+ }
+ else
+ {
+ string prevVersion = Singleton.verion;
+ string installedVersion = VERSION;
+ int versionComparision = Helper.CompareVersions(installedVersion, prevVersion);
+ if (versionComparision != 0)
+ {
+ config.verion = VERSION;
+ config.Save();
+ }
+ if (versionComparision == 1)
+ {
+ Settings.updatedPopup(versionComparision);
+ }
+ else if (versionComparision == -1)
+ {
+ config.OnUpgrade(prevVersion);
+ Debug.Log(">>> Thry Editor has been updated to version " + installedVersion);
+ }
+ }
+ }
+
+ public static Config Singleton
+ {
+ get
+ {
+ if (config == null)
+ {
+ if (File.Exists(PATH_CONFIG_FILE))
+ config = JsonUtility.FromJson<Config>(FileHelper.ReadFileIntoString(PATH_CONFIG_FILE));
+ else
+ config = new Config().Save();
+ }
+ return config;
+ }
+ }
+
+ //actual config class
+ public TextureDisplayType default_texture_type = TextureDisplayType.small;
+ public bool showRenderQueue = true;
+ public bool showManualReloadButton = false;
+ public bool allowCustomLockingRenaming = false;
+ public bool autoMarkPropertiesAnimated = true;
+
+ public string locale = "English";
+
+ public string gradient_name = "gradient_<hash>.png";
+
+ public bool autoSetAnchorOverride = true;
+ public HumanBodyBones humanBoneAnchor = HumanBodyBones.Spine;
+ public string anchorOverrideObjectName = "AutoAnchorObject";
+ public bool autoSetAnchorAskedOnce = false;
+
+ public string verion = VERSION;
+
+ public Config Save()
+ {
+ FileHelper.WriteStringToFile(JsonUtility.ToJson(this), PATH_CONFIG_FILE);
+ return this;
+ }
+
+ private void OnUpgrade(string oldVersionString)
+ {
+ Version newVersion = new Version(VERSION);
+ Version oldVersion = new Version(oldVersionString);
+
+ //Upgrade locking valuesd from Animated property to tags
+ if (newVersion >= "2.11.0" && oldVersion > "2.0" && oldVersion < "2.11.0")
+ {
+ ShaderOptimizer.UpgradeAnimatedPropertiesToTagsOnAllMaterials();
+ }
+ }
+ }
+
+ public class Version
+ {
+ private string value;
+
+ public Version(string s)
+ {
+ if (string.IsNullOrEmpty(s)) s = "0";
+ this.value = s;
+ }
+
+ public static bool operator ==(Version x, Version y)
+ {
+ return Helper.CompareVersions(x.value, y.value) == 0;
+ }
+
+ public static bool operator !=(Version x, Version y)
+ {
+ return Helper.CompareVersions(x.value, y.value) != 0;
+ }
+
+ public static bool operator >(Version x, Version y)
+ {
+ return Helper.CompareVersions(x.value, y.value) == -1;
+ }
+
+ public static bool operator <(Version x, Version y)
+ {
+ return Helper.CompareVersions(x.value, y.value) == 1;
+ }
+
+ public static bool operator >=(Version x, Version y)
+ {
+ return Helper.CompareVersions(x.value, y.value) < 1;
+ }
+
+ public static bool operator <=(Version x, Version y)
+ {
+ return Helper.CompareVersions(x.value, y.value) > -1;
+ }
+
+ public static bool operator ==(Version x, string y)
+ {
+ return Helper.CompareVersions(x.value, y) == 0;
+ }
+
+ public static bool operator !=(Version x, string y)
+ {
+ return Helper.CompareVersions(x.value, y) != 0;
+ }
+
+ public static bool operator >(Version x, string y)
+ {
+ return Helper.CompareVersions(x.value, y) == -1;
+ }
+
+ public static bool operator <(Version x, string y)
+ {
+ return Helper.CompareVersions(x.value, y) == 1;
+ }
+
+ public static bool operator >=(Version x, string y)
+ {
+ return Helper.CompareVersions(x.value, y) < 1;
+ }
+
+ public static bool operator <=(Version x, string y)
+ {
+ return Helper.CompareVersions(x.value, y) > -1;
+ }
+
+ public override bool Equals(object o)
+ {
+ if (o is Version) return this == (o as Version);
+ if (o is string) return this == (o as string);
+ return false;
+ }
+
+ public override string ToString()
+ {
+ return value;
+ }
+
+ public override int GetHashCode()
+ {
+ return base.GetHashCode();
+ }
+ }
+}
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Config.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Config.cs.meta
new file mode 100644
index 00000000..e31eeacb
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Config.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 5b48f91946ace6944a90d8313e080259
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/DataStructs.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/DataStructs.cs
new file mode 100644
index 00000000..4fabc3df
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/DataStructs.cs
@@ -0,0 +1,630 @@
+// Material/Shader Inspector for Unity 2017/2018
+// Copyright (C) 2019 Thryrallo
+
+using System.Collections;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+using UnityEditor;
+using UnityEngine;
+using UnityEngine.Rendering;
+
+namespace Thry
+{
+ #region Constants
+ public class PATH
+ {
+ public const string TEXTURES_DIR = "Assets/textures";
+ public const string RSP_NEEDED_PATH = "Assets/";
+
+ public const string DELETING_DIR = "Thry/trash";
+
+ public const string PERSISTENT_DATA = "Thry/persistent_data";
+ public const string AFTER_COMPILE_DATA = "Thry/after_compile_data";
+ public const string MATERIALS_BACKUP_FILE = "Thry/materialsBackup";
+ public const string THRY_EDITOR_SHADERS = "Thry/shaders";
+
+ public const string GRADIENT_INFO_FILE = "Thry/gradients";
+ public const string TEXT_INFO_FILE = "Thry/text_textures";
+ public const string MODULES_LOCATION__DATA = "Thry/modules_location_data";
+
+ public const string LINKED_MATERIALS_FILE = "Thry/linked_materials.json";
+
+ public const string TEMP_VRC_SDK_PACKAGE = "./vrc_sdk_package.unitypackage";
+ }
+
+ public class URL
+ {
+ public const string MODULE_COLLECTION = "https://thryeditor.thryrallo.de/files/modules.json";
+ public const string SETTINGS_MESSAGE_URL = "http://thryeditor.thryrallo.de/message.json";
+
+ public const string DATA_SHARE_SEND = "http://thryeditor.thryrallo.de/send_analytics.php";
+ public const string DATA_SHARE_GET_MY_DATA = "https://thryeditor.thryrallo.de/get_my_data.php";
+ public const string COUNT_PROJECT = "http://thryeditor.thryrallo.de/count_project.php";
+ public const string COUNT_USER = "http://thryeditor.thryrallo.de/count_user.php";
+ }
+
+ public class DEFINE_SYMBOLS
+ {
+ public const string IMAGING_EXISTS = "IMAGING_DLL_EXISTS";
+ }
+
+ public class RESOURCE_NAME
+ {
+ public const string RECT = "thry_rect";
+ public const string ICON_NAME_LINK = "thryEditor_link";
+ public const string ICON_NAME_THRY = "thryEditor_iconThry";
+ }
+ #endregion
+
+ public class DrawingData
+ {
+ public static TextureProperty CurrentTextureProperty;
+ public static Rect LastGuiObjectRect;
+ public static Rect LastGuiObjectHeaderRect;
+ public static Rect TooltipCheckRect;
+ public static bool LastPropertyUsedCustomDrawer;
+ public static bool LastPropertyDoesntAllowAnimation;
+ public static DrawerType LastPropertyDrawerType;
+ public static MaterialPropertyDrawer LastPropertyDrawer;
+ public static bool IsEnabled = true;
+
+ public static ShaderPart LastInitiatedPart;
+
+ public static void ResetLastDrawerData()
+ {
+ LastPropertyUsedCustomDrawer = false;
+ LastPropertyDoesntAllowAnimation = false;
+ LastPropertyDrawer = null;
+ LastPropertyDrawerType = DrawerType.None;
+ }
+ }
+
+ public enum DrawerType
+ {
+ None, Header
+ }
+
+ public class GradientData
+ {
+ public Texture PreviewTexture;
+ public Gradient Gradient;
+ }
+
+ public enum TextureDisplayType
+ {
+ small, big, stylized_big
+ }
+
+ //--------------Shader Data Structs--------------------
+
+ #region In Shader Data
+ public class PropertyOptions
+ {
+ public int offset = 0;
+ public string tooltip = "";
+ public DefineableAction altClick;
+ public DefineableAction onClick;
+ public DefineableCondition condition_show = new DefineableCondition();
+ public string condition_showS;
+ public DefineableCondition condition_enable = null;
+ public PropertyValueAction[] on_value_actions;
+ public string on_value;
+ public DefineableAction[] actions;
+ public ButtonData button_help;
+ public TextureData texture;
+ public string[] reference_properties;
+ public string reference_property;
+ public bool force_texture_options = false;
+ public bool hide_in_inspector = false;
+ public bool is_visible_simple = false;
+ public string file_name;
+ public string remote_version_url;
+ public string generic_string;
+ }
+
+ public class ButtonData
+ {
+ public string text = "";
+ public TextureData texture = null;
+ public DefineableAction action = new DefineableAction();
+ public string hover = "";
+ public DefineableCondition condition_show = new DefineableCondition();
+ }
+
+ public class TextureData
+ {
+ public string name = null;
+
+ public int width = 128;
+ public int height = 128;
+
+ public char channel = 'r';
+
+ public int ansioLevel = 1;
+ public FilterMode filterMode = FilterMode.Bilinear;
+ public TextureWrapMode wrapMode = TextureWrapMode.Repeat;
+
+ public void ApplyModes(Texture texture)
+ {
+ texture.filterMode = filterMode;
+ texture.wrapMode = wrapMode;
+ texture.anisoLevel = ansioLevel;
+ }
+ public void ApplyModes(string path)
+ {
+ TextureImporter importer = (TextureImporter)AssetImporter.GetAtPath(path);
+ importer.filterMode = filterMode;
+ importer.wrapMode = wrapMode;
+ importer.anisoLevel = ansioLevel;
+ importer.SaveAndReimport();
+ }
+
+ private Texture p_loaded_texture;
+ public Texture loaded_texture
+ {
+ get
+ {
+ if (p_loaded_texture == null)
+ {
+ string path = FileHelper.FindFile(name, "texture");
+ if (path != null)
+ p_loaded_texture = AssetDatabase.LoadAssetAtPath<Texture>(path);
+ else
+ p_loaded_texture = new Texture2D(1, 1);
+ }
+ return p_loaded_texture;
+ }
+ }
+ }
+
+ public class PropertyValueAction
+ {
+ public string value;
+ public DefineableAction[] actions;
+
+ public bool Execute(MaterialProperty p)
+ {
+ if(
+ (p.type == MaterialProperty.PropType.Float && p.floatValue.ToString() == value) ||
+ (p.type == MaterialProperty.PropType.Range && p.floatValue.ToString() == value) ||
+ (p.type == MaterialProperty.PropType.Color && p.colorValue.ToString() == value) ||
+ (p.type == MaterialProperty.PropType.Vector && p.vectorValue.ToString() == value) ||
+ (p.type == MaterialProperty.PropType.Texture && ((p.textureValue == null) == (value == "0"))) ||
+ (p.type == MaterialProperty.PropType.Texture && ((p.textureValue != null) == (value == "1"))) ||
+ (p.type == MaterialProperty.PropType.Texture && (p.textureValue != null && p.textureValue.name == value))
+ )
+ {
+ foreach (DefineableAction a in actions)
+ a.Perform();
+ return true;
+ }
+ return false;
+ }
+
+ private static PropertyValueAction ParseForThryParser(string s)
+ {
+ return Parse(s);
+ }
+ // value,property1=value1,property2=value2
+ public static PropertyValueAction Parse(string s)
+ {
+ s = s.Trim();
+ string[] parts = s.Split(',');
+ if (parts.Length > 0)
+ {
+ PropertyValueAction propaction = new PropertyValueAction();
+ propaction.value = parts[0];
+ List<DefineableAction> actions = new List<DefineableAction>();
+ for (int i = 1; i < parts.Length; i++)
+ {
+ actions.Add(DefineableAction.Parse(parts[i]));
+ }
+ propaction.actions = actions.ToArray();
+ return propaction;
+ }
+ return null;
+ }
+
+ private static PropertyValueAction[] ParseToArrayForThryParser(string s)
+ {
+ return ParseToArray(s);
+ }
+ public static PropertyValueAction[] ParseToArray(string s)
+ {
+ //s = v,p1=v1,p2=v2;v3
+ List<PropertyValueAction> propactions = new List<PropertyValueAction>();
+ string[] parts = s.Split(';');
+ foreach (string p in parts)
+ {
+ PropertyValueAction propertyValueAction = PropertyValueAction.Parse(p);
+ if (propertyValueAction != null)
+ propactions.Add(propertyValueAction);
+ }
+ return propactions.ToArray();
+ }
+ }
+
+ public class DefineableAction
+ {
+ public DefineableActionType type = DefineableActionType.NONE;
+ public string data = "";
+ public void Perform()
+ {
+ switch (type)
+ {
+ case DefineableActionType.URL:
+ Application.OpenURL(data);
+ break;
+ case DefineableActionType.SET_PROPERTY:
+ string[] set = Regex.Split(data, @"=");
+ if (set.Length > 1)
+ MaterialHelper.SetMaterialValue(set[0].Trim(), set[1].Trim());
+ break;
+ case DefineableActionType.SET_TAG:
+ string[] keyValue = Regex.Split(data, @"=");
+ foreach (Material m in ShaderEditor.Active.Materials)
+ m.SetOverrideTag(keyValue[0].Trim(), keyValue[1].Trim());
+ break;
+ case DefineableActionType.SET_SHADER:
+ Shader shader = Shader.Find(data);
+ if (shader != null)
+ {
+ foreach (Material m in ShaderEditor.Active.Materials)
+ m.shader = shader;
+ }
+ break;
+ }
+ }
+
+ private static DefineableAction ParseForThryParser(string s)
+ {
+ return Parse(s);
+ }
+ public static DefineableAction Parse(string s)
+ {
+ s = s.Trim();
+ DefineableAction action = new DefineableAction();
+ if (s.StartsWith("http") || s.StartsWith("www"))
+ {
+ action.type = DefineableActionType.URL;
+ action.data = s;
+ }
+ else if (s.StartsWith("tag::"))
+ {
+ action.type = DefineableActionType.SET_TAG;
+ action.data = s.Replace("tag::", "");
+ }
+ else if (s.StartsWith("shader="))
+ {
+ action.type = DefineableActionType.SET_SHADER;
+ action.data = s.Replace("shader=", "");
+ }
+ else if (s.Contains("="))
+ {
+ action.type = DefineableActionType.SET_PROPERTY;
+ action.data = s;
+ }
+ return action;
+ }
+
+ public static DefineableAction ParseDrawerParameter(string s)
+ {
+ s = s.Trim();
+ DefineableAction action = new DefineableAction();
+ if (s.StartsWith("youtube#"))
+ {
+ action.type = DefineableActionType.URL;
+ action.data = "https://www.youtube.com/watch?v="+s.Substring(8);
+ }
+ return action;
+ }
+
+ public override string ToString()
+ {
+ return $"{{{type},{data}}}";
+ }
+ }
+
+ public enum DefineableActionType
+ {
+ NONE,
+ URL,
+ SET_PROPERTY,
+ SET_SHADER,
+ SET_TAG
+ }
+
+ public class DefineableCondition
+ {
+ public DefineableConditionType type = DefineableConditionType.NONE;
+ public string data = "";
+ public DefineableCondition condition1;
+ public DefineableCondition condition2;
+
+ CompareType _compareType;
+ string _obj;
+ ShaderProperty _propertyObj;
+
+ string _value;
+ float _floatValue;
+
+ bool _hasConstantValue;
+ bool _constantValue;
+
+ bool _isInit = false;
+ public void Init()
+ {
+ if (_isInit) return;
+ _hasConstantValue = true;
+ if (type == DefineableConditionType.NONE) { _constantValue = true; }
+ else if (type == DefineableConditionType.TRUE) { _constantValue = true; }
+ else if (type == DefineableConditionType.FALSE) { _constantValue = false; }
+ else
+ {
+ var (compareType, compareString) = GetComparetor();
+ _compareType = compareType;
+
+ string[] parts = Regex.Split(data, compareString);
+ _obj = parts[0];
+ _value = parts[parts.Length - 1];
+
+ _floatValue = Parser.ParseFloat(_value);
+ if (ShaderEditor.Active != null && ShaderEditor.Active.PropertyDictionary.ContainsKey(_obj))
+ _propertyObj = ShaderEditor.Active.PropertyDictionary[_obj];
+
+ if (type == DefineableConditionType.EDITOR_VERSION) InitEditorVersion();
+ else if (type == DefineableConditionType.VRC_SDK_VERSION) InitVRCSDKVersion();
+ else _hasConstantValue = false;
+ }
+
+ _isInit = true;
+ }
+
+ void InitEditorVersion()
+ {
+ int c_ev = Helper.CompareVersions(Config.Singleton.verion, _value);
+ if (_compareType == CompareType.EQUAL) _constantValue = c_ev == 0;
+ if (_compareType == CompareType.NOT_EQUAL) _constantValue = c_ev != 0;
+ if (_compareType == CompareType.SMALLER) _constantValue = c_ev == 1;
+ if (_compareType == CompareType.BIGGER) _constantValue = c_ev == -1;
+ if (_compareType == CompareType.BIGGER_EQ) _constantValue = c_ev == -1 || c_ev == 0;
+ if (_compareType == CompareType.SMALLER_EQ) _constantValue = c_ev == 1 || c_ev == 0;
+ }
+
+ void InitVRCSDKVersion()
+ {
+ if (VRCInterface.Get().Sdk_information.type == VRCInterface.VRC_SDK_Type.NONE)
+ {
+ _constantValue = false;
+ return;
+ }
+ int c_vrc = Helper.CompareVersions(VRCInterface.Get().Sdk_information.installed_version, _value);
+ if (_compareType == CompareType.EQUAL) _constantValue = c_vrc == 0;
+ if (_compareType == CompareType.NOT_EQUAL) _constantValue = c_vrc != 0;
+ if (_compareType == CompareType.SMALLER) _constantValue = c_vrc == 1;
+ if (_compareType == CompareType.BIGGER) _constantValue = c_vrc == -1;
+ if (_compareType == CompareType.BIGGER_EQ) _constantValue = c_vrc == -1 || c_vrc == 0;
+ if (_compareType == CompareType.SMALLER_EQ) _constantValue = c_vrc == 1 || c_vrc == 0;
+ }
+
+ public bool Test()
+ {
+ Init();
+ if (_hasConstantValue) return _constantValue;
+
+ switch (type)
+ {
+ case DefineableConditionType.PROPERTY_BOOL:
+ if (_propertyObj == null) return false;
+ if (_compareType == CompareType.NONE) return _propertyObj.MaterialProperty.floatValue == 1;
+ if (_compareType == CompareType.EQUAL) return _propertyObj.MaterialProperty.floatValue == _floatValue;
+ if (_compareType == CompareType.NOT_EQUAL) return _propertyObj.MaterialProperty.floatValue != _floatValue;
+ if (_compareType == CompareType.SMALLER) return _propertyObj.MaterialProperty.floatValue < _floatValue;
+ if (_compareType == CompareType.BIGGER) return _propertyObj.MaterialProperty.floatValue > _floatValue;
+ if (_compareType == CompareType.BIGGER_EQ) return _propertyObj.MaterialProperty.floatValue >= _floatValue;
+ if (_compareType == CompareType.SMALLER_EQ) return _propertyObj.MaterialProperty.floatValue <= _floatValue;
+ break;
+ case DefineableConditionType.TEXTURE_SET:
+ if (_propertyObj == null) return false;
+ return _propertyObj.MaterialProperty.textureValue != null;
+ case DefineableConditionType.DROPDOWN:
+ if (_propertyObj == null) return false;
+ if (_compareType == CompareType.NONE) return _propertyObj.MaterialProperty.floatValue == 1;
+ if (_compareType == CompareType.EQUAL) return "" + _propertyObj.MaterialProperty.floatValue == _value;
+ if (_compareType == CompareType.NOT_EQUAL) return "" + _propertyObj.MaterialProperty.floatValue != _value;
+ break;
+ case DefineableConditionType.AND:
+ if(condition1!=null&&condition2!=null) return condition1.Test() && condition2.Test();
+ break;
+ case DefineableConditionType.OR:
+ if (condition1 != null && condition2 != null) return condition1.Test() || condition2.Test();
+ break;
+ }
+
+ return true;
+ }
+ private (CompareType,string) GetComparetor()
+ {
+ if (data.Contains("=="))
+ return (CompareType.EQUAL,"==");
+ if (data.Contains("!="))
+ return (CompareType.NOT_EQUAL,"!=");
+ if (data.Contains(">="))
+ return (CompareType.BIGGER_EQ,">=");
+ if (data.Contains("<="))
+ return (CompareType.SMALLER_EQ,"<=");
+ if (data.Contains(">"))
+ return (CompareType.BIGGER,">");
+ if (data.Contains("<"))
+ return (CompareType.SMALLER,"<");
+ return (CompareType.NONE,"##");
+ }
+
+ public override string ToString()
+ {
+ switch (type)
+ {
+ case DefineableConditionType.PROPERTY_BOOL:
+ return data;
+ case DefineableConditionType.EDITOR_VERSION:
+ return "EDITOR_VERSION" + data;
+ case DefineableConditionType.VRC_SDK_VERSION:
+ return "VRC_SDK_VERSION" + data;
+ case DefineableConditionType.AND:
+ if (condition1 != null && condition2 != null) return "("+condition1.ToString() + "&&" + condition2.ToString()+")";
+ break;
+ case DefineableConditionType.OR:
+ if (condition1 != null && condition2 != null) return "("+condition1.ToString()+"||"+condition2.ToString()+")";
+ break;
+ }
+ return "";
+ }
+
+ private static DefineableCondition ParseForThryParser(string s)
+ {
+ return Parse(s);
+ }
+ public static DefineableCondition Parse(string s)
+ {
+ s = Strip(s);
+
+ int depth = 0;
+ for (int i = 0; i < s.Length - 1; i++)
+ {
+ char c = s[i];
+ char cc = s[i + 1];
+ if (c == '(')
+ depth++;
+ else if (c == ')')
+ depth--;
+
+ if (depth == 0)
+ {
+ if (c == '&' && cc == '&')
+ {
+ DefineableCondition con = new DefineableCondition();
+ con.type = DefineableConditionType.AND;
+ con.condition1 = Parse(s.Substring(0, i));
+ con.condition2 = Parse(s.Substring(i + 2, s.Length - i - 2));
+ return con;
+ }
+ if (c == '|' && cc == '|')
+ {
+ DefineableCondition con = new DefineableCondition();
+ con.type = DefineableConditionType.OR;
+ con.condition1 = Parse(s.Substring(0, i));
+ con.condition2 = Parse(s.Substring(i + 2, s.Length - i - 2));
+ return con;
+ }
+ }
+ }
+ for (int i = 0; i < s.Length - 1; i++)
+ {
+ char c = s[i];
+ char cc = s[i + 1];
+ if (c == '(')
+ depth++;
+ else if (c == ')')
+ depth--;
+
+ if (depth == 0)
+ {
+ if (c == '>' || c=='<' || c=='=' || c == '!')
+ {
+ DefineableCondition con = new DefineableCondition();
+ con.data = s;
+ con.type = DefineableConditionType.PROPERTY_BOOL;
+ if (s.StartsWith("VRCSDK"))
+ {
+ con.type = DefineableConditionType.VRC_SDK_VERSION;
+ con.data = s.Replace("VRCSDK", "");
+ }else if (s.StartsWith("ThryEditor"))
+ {
+ con.type = DefineableConditionType.VRC_SDK_VERSION;
+ con.data = s.Replace("ThryEditor", "");
+ }
+ return con;
+ }
+ }
+ }
+ return new DefineableCondition();
+ }
+
+ private static string Strip(string s)
+ {
+ s = s.Trim();
+ if (s.StartsWith("(") == false)
+ return s;
+ bool stripKlammer = true;
+ int depth = 0;
+ int i = 0;
+ foreach (char c in s)
+ {
+ if (c == '(')
+ depth++;
+ else if (c == ')')
+ depth--;
+ if (depth == 0 && i != 0 && i != s.Length - 1)
+ stripKlammer = false;
+ i++;
+ }
+ if (stripKlammer)
+ return Strip(s.Substring(1, s.Length - 2));
+ return s;
+ }
+ }
+
+ enum CompareType { NONE,BIGGER,SMALLER,EQUAL,NOT_EQUAL,BIGGER_EQ,SMALLER_EQ }
+
+ public enum DefineableConditionType
+ {
+ NONE,
+ TRUE,
+ FALSE,
+ PROPERTY_BOOL,
+ EDITOR_VERSION,
+ VRC_SDK_VERSION,
+ TEXTURE_SET,
+ DROPDOWN,
+ AND,
+ OR
+ }
+
+ #endregion
+
+ #region Module Data
+
+ public class Module
+ {
+ public string id;
+ public string url = "";
+ public string author;
+ public string path;
+ public bool is_being_installed_or_removed = false;
+ public bool available_requirement_fullfilled = true;
+ public bool update_available = false;
+ public ModuleLocationData location_data;
+ public ModuleInfo available_module = null;
+ public ModuleInfo installed_module = null;
+ public bool ui_expanded = false;
+ }
+
+ public class ModuleInfo
+ {
+ public string name = "";
+ public string version = "0";
+ public string description = "";
+ public string classname = "";
+ public DefineableCondition requirement;
+ public List<string> files;
+ }
+
+ public class ModuleLocationData
+ {
+ public string guid;
+ public string classname;
+ public string[] files;
+ }
+
+ #endregion
+} \ No newline at end of file
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/DataStructs.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/DataStructs.cs.meta
new file mode 100644
index 00000000..16085f5b
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/DataStructs.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 42f9390784d11e84bbf6366e794e6b20
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Drawers.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Drawers.cs
new file mode 100644
index 00000000..da515b12
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Drawers.cs
@@ -0,0 +1,1501 @@
+// Material/Shader Inspector for Unity 2017/2018
+// Copyright (C) 2019 Thryrallo
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text.RegularExpressions;
+using UnityEditor;
+using UnityEngine;
+
+namespace Thry
+{
+ #region Texture Drawers
+ public class ThryTextureDrawer : MaterialPropertyDrawer
+ {
+ public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
+ {
+ GuiHelper.ConfigTextureProperty(position, prop, label, editor, ((TextureProperty)ShaderEditor.Active.CurrentProperty).hasScaleOffset);
+ }
+
+ public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
+ {
+ DrawingData.LastPropertyUsedCustomDrawer = true;
+ return base.GetPropertyHeight(prop, label, editor);
+ }
+ }
+
+ public class SmallTextureDrawer : MaterialPropertyDrawer
+ {
+ public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
+ {
+ GuiHelper.SmallTextureProperty(position, prop, label, editor, ((TextureProperty)ShaderEditor.Active.CurrentProperty).hasScaleOffset);
+ }
+
+ public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
+ {
+ DrawingData.LastPropertyUsedCustomDrawer = true;
+ return base.GetPropertyHeight(prop, label, editor);
+ }
+ }
+
+ public class BigTextureDrawer : MaterialPropertyDrawer
+ {
+ public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
+ {
+ GuiHelper.BigTextureProperty(position, prop, label, editor, ((TextureProperty)ShaderEditor.Active.CurrentProperty).hasScaleOffset);
+ }
+
+ public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
+ {
+ DrawingData.LastPropertyUsedCustomDrawer = true;
+ return base.GetPropertyHeight(prop, label, editor);
+ }
+ }
+
+ public class StylizedBigTextureDrawer : MaterialPropertyDrawer
+ {
+ public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
+ {
+ GuiHelper.StylizedBigTextureProperty(position, prop, label, editor, ((TextureProperty)ShaderEditor.Active.CurrentProperty).hasScaleOffset);
+ }
+
+ public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
+ {
+ DrawingData.LastPropertyUsedCustomDrawer = true;
+ return base.GetPropertyHeight(prop, label, editor);
+ }
+ }
+ #endregion
+
+ #region Special Texture Drawers
+ public class CurveDrawer : MaterialPropertyDrawer
+ {
+ public AnimationCurve curve;
+ public EditorWindow window;
+ public Texture2D texture;
+ public bool saved = true;
+ public TextureData imageData;
+
+ public CurveDrawer()
+ {
+ curve = new AnimationCurve();
+ }
+
+ private void Init()
+ {
+ if (imageData == null)
+ {
+ if (ShaderEditor.Active.CurrentProperty.Options.texture == null)
+ imageData = new TextureData();
+ else
+ imageData = ShaderEditor.Active.CurrentProperty.Options.texture;
+ }
+ }
+
+ public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
+ {
+ Init();
+ Rect border_position = new Rect(position.x + EditorGUIUtility.labelWidth - 15, position.y, position.width - EditorGUIUtility.labelWidth - position.x + 15, position.height);
+
+ EditorGUI.BeginChangeCheck();
+ curve = EditorGUI.CurveField(border_position, curve);
+ if (EditorGUI.EndChangeCheck())
+ {
+ UpdateCurveTexture(prop);
+ }
+
+ GuiHelper.SmallTextureProperty(position, prop, label, editor, DrawingData.CurrentTextureProperty.hasFoldoutProperties);
+
+ CheckWindowForCurveEditor();
+
+ if (window == null && !saved)
+ Save(prop);
+ }
+
+ private void UpdateCurveTexture(MaterialProperty prop)
+ {
+ texture = Converter.CurveToTexture(curve, imageData);
+ prop.textureValue = texture;
+ saved = false;
+ }
+
+ private void CheckWindowForCurveEditor()
+ {
+ string windowName = "";
+ if (EditorWindow.focusedWindow != null)
+ windowName = EditorWindow.focusedWindow.titleContent.text;
+ bool isCurveEditor = windowName == "Curve";
+ if (isCurveEditor)
+ window = EditorWindow.focusedWindow;
+ }
+
+ private void Save(MaterialProperty prop)
+ {
+ Debug.Log(prop.textureValue.ToString());
+ Texture saved_texture = TextureHelper.SaveTextureAsPNG(texture, PATH.TEXTURES_DIR + "curves/" + curve.GetHashCode() + ".png", null);
+ prop.textureValue = saved_texture;
+ saved = true;
+ }
+
+ public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
+ {
+ DrawingData.LastPropertyUsedCustomDrawer = true;
+ return base.GetPropertyHeight(prop, label, editor);
+ }
+ }
+ public class ThryExternalTextureToolDrawer : MaterialPropertyDrawer
+ {
+ string _toolTypeName;
+ string _toolHeader;
+
+ Type t_ExternalToolType;
+ MethodInfo _onGui;
+ object _externalTool;
+ MaterialProperty _prop;
+
+ bool _isTypeLoaded;
+ bool _doesExternalTypeExist;
+ bool _isInit;
+ bool _showTool;
+
+ public ThryExternalTextureToolDrawer(string toolHeader, string toolTypeName)
+ {
+ this._toolTypeName = toolTypeName;
+ this._toolHeader = toolHeader;
+ }
+
+ public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
+ {
+ LoadType();
+ if (_doesExternalTypeExist)
+ {
+ _prop = prop;
+ GuiHelper.SmallTextureProperty(position, prop, label, editor, DrawingData.CurrentTextureProperty.hasFoldoutProperties, ExternalGUI);
+ }
+ else
+ {
+ GuiHelper.SmallTextureProperty(position, prop, label, editor, DrawingData.CurrentTextureProperty.hasFoldoutProperties);
+ }
+ }
+
+ void ExternalGUI()
+ {
+ if (GUI.Button(EditorGUI.IndentedRect(EditorGUILayout.GetControlRect()), _toolHeader)) _showTool = !_showTool;
+ if (_showTool)
+ {
+ Init();
+
+ int indent = EditorGUI.indentLevel;
+ GuiHelper.BeginCustomIndentLevel(0);
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(indent * 15);
+ GUILayout.BeginVertical();
+ _onGui.Invoke(_externalTool, new object[0]);
+ GUILayout.EndVertical();
+ GUILayout.EndHorizontal();
+ GuiHelper.EndCustomIndentLevel();
+ }
+ }
+
+ public void LoadType()
+ {
+ if (_isTypeLoaded) return;
+ t_ExternalToolType = AppDomain.CurrentDomain.GetAssemblies().Select(a => a.GetType(_toolTypeName)).Where(t => t != null).FirstOrDefault();
+ _doesExternalTypeExist = t_ExternalToolType != null;
+ _isTypeLoaded = true;
+ }
+
+ public void Init()
+ {
+ if (_isInit) return;
+ if (_isTypeLoaded && _doesExternalTypeExist)
+ {
+ _onGui = t_ExternalToolType.GetMethod("OnGUI", BindingFlags.NonPublic | BindingFlags.Instance);
+ _externalTool = ScriptableObject.CreateInstance(t_ExternalToolType);
+ EventInfo eventTextureGenerated = t_ExternalToolType.GetEvent("TextureGenerated");
+ if (eventTextureGenerated != null)
+ eventTextureGenerated.AddEventHandler(_externalTool, new EventHandler(TextureGenerated));
+ }
+ _isInit = true;
+ }
+
+ void TextureGenerated(object sender, EventArgs args)
+ {
+ if (args != null && args.GetType().GetField("generated_texture") != null)
+ {
+ Texture2D generated = args.GetType().GetField("generated_texture").GetValue(args) as Texture2D;
+ _prop.textureValue = generated;
+ }
+ }
+
+ public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
+ {
+ DrawingData.LastPropertyUsedCustomDrawer = true;
+ return base.GetPropertyHeight(prop, label, editor);
+ }
+ }
+
+ public class ThryRGBAPackerDrawer : MaterialPropertyDrawer
+ {
+
+ class ThryRGBAPackerData
+ {
+ public Texture _previousTexture;
+ public Texture2D _packedTexture;
+
+ public PackerChannelConfig _input_r;
+ public PackerChannelConfig _input_g;
+ public PackerChannelConfig _input_b;
+ public PackerChannelConfig _input_a;
+
+ public bool _isInit;
+ public bool _hasConfigChanged;
+ public bool _hasTextureChanged;
+ public long _lastConfirmTime;
+ }
+
+ Dictionary<UnityEngine.Object, ThryRGBAPackerData> materialPackerData = new Dictionary<UnityEngine.Object, ThryRGBAPackerData>();
+
+ MaterialProperty _prop;
+ ThryRGBAPackerData _current;
+
+ string _label1;
+ string _label2;
+ string _label3;
+ string _label4;
+ bool _firstTextureIsRGB;
+ bool _makeSRGB = true;
+
+ public ThryRGBAPackerDrawer(string label1, string label2, string label3, string label4, float sRGB)
+ {
+ _label1 = label1;
+ _label2 = label2;
+ _label3 = label3;
+ _label4 = label4;
+ _makeSRGB = sRGB == 1;
+ }
+
+ public ThryRGBAPackerDrawer(string label1, string label2, float sRGB) : this(label1, label2, null, null, sRGB) { }
+ public ThryRGBAPackerDrawer(string label1, string label2, string label3, float sRGB) : this(label1, label2, label3, null, sRGB) { }
+
+ public ThryRGBAPackerDrawer(string label1, string label2) : this(label1,label2,null, null, 0){}
+ public ThryRGBAPackerDrawer(string label1, string label2, string label3) : this(label1,label2,label3, null, 0){}
+ public ThryRGBAPackerDrawer(string label1, string label2, string label3, string label4) : this(label1,label2,label3,label4, 0){}
+
+ public ThryRGBAPackerDrawer(float firstTextureIsRGB, string label1, string label2) : this(label1, label2, null, null, 0)
+ {
+ _firstTextureIsRGB = firstTextureIsRGB == 1;
+ }
+ public ThryRGBAPackerDrawer(float firstTextureIsRGB, string label1, string label2, float sRGB) : this(label1, label2, null, null, sRGB)
+ {
+ _firstTextureIsRGB = firstTextureIsRGB == 1;
+ }
+
+ public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
+ {
+ _prop = prop;
+ if (materialPackerData.ContainsKey(_prop.targets[0]) == false) materialPackerData[_prop.targets[0]] = new ThryRGBAPackerData();
+ _current = materialPackerData[_prop.targets[0]];
+ GuiHelper.SmallTextureProperty(position, prop, label, editor, true, TexturePackerGUI);
+ if (_prop.textureValue != _current._packedTexture) _current._previousTexture = _prop.textureValue;
+ }
+
+ bool DidTextureGetEdit(PackerChannelConfig data)
+ {
+ if (data.Texture == null) return false;
+ string path = AssetDatabase.GetAssetPath(data.Texture);
+ if (System.IO.File.Exists(path) == false) return false;
+ long lastEditTime = Helper.DatetimeToUnixSeconds(System.IO.File.GetLastWriteTime(path));
+ bool hasBeenEdited = lastEditTime > _current._lastConfirmTime && lastEditTime != data.LastHandledTextureEditTime;
+ data.LastHandledTextureEditTime = lastEditTime;
+ if (hasBeenEdited) data.DoReloadUncompressedTexture = true;
+ return hasBeenEdited;
+ }
+
+ void TexturePackerGUI()
+ {
+ Init();
+ EditorGUI.BeginChangeCheck();
+ _current._input_r = TexturePackerSlotGUI(_current._input_r, _label1);
+ _current._input_g = TexturePackerSlotGUI(_current._input_g, _label2);
+ if (_label3 != null) _current._input_b = TexturePackerSlotGUI(_current._input_b, _label3);
+ if (_label4 != null) _current._input_a = TexturePackerSlotGUI(_current._input_a, _label4);
+ bool changeCheck = EditorGUI.EndChangeCheck();
+ changeCheck |= DidTextureGetEdit(_current._input_r);
+ changeCheck |= DidTextureGetEdit(_current._input_g);
+ changeCheck |= DidTextureGetEdit(_current._input_b);
+ changeCheck |= DidTextureGetEdit(_current._input_a);
+ if(changeCheck)
+ {
+ _current._hasConfigChanged = true;
+ Save();
+ Pack();
+ }
+
+ Rect buttonRect = EditorGUI.IndentedRect(EditorGUILayout.GetControlRect());
+ buttonRect.width /= 2;
+ EditorGUI.BeginDisabledGroup(!_current._hasConfigChanged);
+ if (GUI.Button(buttonRect, "Confirm Merge")) Confirm();
+ buttonRect.x += buttonRect.width;
+ EditorGUI.EndDisabledGroup();
+ EditorGUI.BeginDisabledGroup(!_current._hasTextureChanged);
+ if (GUI.Button(buttonRect, "Revert")) Revert();
+ EditorGUI.EndDisabledGroup();
+ }
+
+ Texture test;
+
+ PackerChannelConfig TexturePackerSlotGUI(PackerChannelConfig input, string label)
+ {
+ Rect totalRect = EditorGUILayout.GetControlRect(false);
+ totalRect = EditorGUI.IndentedRect(totalRect);
+ Rect r = totalRect;
+
+ int ind = EditorGUI.indentLevel;
+ EditorGUI.indentLevel = 0;
+
+ float texWidth = Math.Max(50, r.width - 130 - 30) - 5;
+ r.x = totalRect.x;
+ r.width = 30;
+ input.Texture = EditorGUI.ObjectField(r, input.Texture, typeof(Texture2D), false) as Texture2D;
+
+ r.x += r.width + 5;
+ r.width = texWidth - 5;
+ EditorGUI.LabelField(r, label);
+
+ if (input.Texture == null)
+ {
+ r.width = 70;
+ r.x = totalRect.x + totalRect.width - r.width;
+ input.Fallback = EditorGUI.FloatField(r, input.Fallback);
+
+ r.width = 60;
+ r.x -= r.width;
+ EditorGUI.LabelField(r, "Fallback:");
+ }
+ else
+ {
+ r.width = 50;
+ r.x = totalRect.x + totalRect.width - r.width;
+ if(!_firstTextureIsRGB || input != _current._input_r)
+ input.Channel = (TextureChannel)EditorGUI.EnumPopup(r, input.Channel);
+
+ r.width = 20;
+ r.x -= r.width;
+ input.Invert = EditorGUI.Toggle(r, input.Invert);
+
+ r.width = 60;
+ r.x -= r.width;
+ EditorGUI.LabelField(r, "Inverted:");
+ }
+
+ EditorGUI.indentLevel = ind;
+
+ return input;
+ }
+
+ void Init()
+ {
+ if (_current._isInit) return;
+ _current._input_r = LoadForChannel(ShaderEditor.Active.Materials[0], _prop.name, "r");
+ _current._input_g = LoadForChannel(ShaderEditor.Active.Materials[0], _prop.name, "g");
+ _current._input_b = LoadForChannel(ShaderEditor.Active.Materials[0], _prop.name, "b");
+ _current._input_a = LoadForChannel(ShaderEditor.Active.Materials[0], _prop.name, "a");
+ _current._lastConfirmTime = long.Parse(ShaderEditor.Active.Materials[0].GetTag(_prop.name + "_texPack_lastConfirmTime", false, "" + Helper.DatetimeToUnixSeconds(DateTime.Now)));
+ _current._previousTexture = _prop.textureValue;
+ _current._isInit = true;
+ }
+
+ void Save()
+ {
+ SaveForChannel(_current._input_r, _prop.name, "r");
+ SaveForChannel(_current._input_g, _prop.name, "g");
+ SaveForChannel(_current._input_b, _prop.name, "b");
+ SaveForChannel(_current._input_a, _prop.name, "a");
+ foreach(Material m in ShaderEditor.Active.Materials)
+ {
+ m.SetOverrideTag(_prop.name + "_texPack_lastConfirmTime", "" +_current._lastConfirmTime);
+ }
+ }
+
+ void SaveForChannel(PackerChannelConfig input, string id, string channel)
+ {
+ foreach (Material m in ShaderEditor.Active.Materials)
+ {
+ if (input.Texture != null) m.SetOverrideTag(id + "_texPack_" + channel + "_guid", AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(input.Texture)));
+ else m.SetOverrideTag(id + "_texPack_" + channel + "_guid", "");
+ m.SetOverrideTag(id + "_texPack_" + channel + "_fallback", input.Fallback.ToString());
+ m.SetOverrideTag(id + "_texPack_" + channel + "_inverted", input.Invert.ToString());
+ m.SetOverrideTag(id + "_texPack_" + channel + "_channel", ((int)input.Channel).ToString());
+ }
+ }
+
+ PackerChannelConfig LoadForChannel(Material m, string id, string channel)
+ {
+ PackerChannelConfig packerChannelConfig = new PackerChannelConfig();
+ packerChannelConfig.Fallback = float.Parse(m.GetTag(id + "_texPack_" + channel + "_fallback", false, "1"));
+ packerChannelConfig.Invert = bool.Parse(m.GetTag(id + "_texPack_" + channel + "_inverted", false, "false"));
+ packerChannelConfig.Channel = (TextureChannel)int.Parse(m.GetTag(id + "_texPack_" + channel + "_channel", false, "4"));
+ string guid = m.GetTag(id + "_texPack_" + channel + "_guid", false, "");
+ if (string.IsNullOrEmpty(guid) == false)
+ {
+ string p = AssetDatabase.GUIDToAssetPath(guid);
+ if (p != null)
+ packerChannelConfig.Texture = AssetDatabase.LoadAssetAtPath<Texture2D>(p);
+ }
+ return packerChannelConfig;
+ }
+
+ void Pack()
+ {
+ int width = 16;
+ int height = 16;
+ //Find max size
+ _current._input_r.FindMaxSize(ref width, ref height);
+ _current._input_g.FindMaxSize(ref width, ref height);
+ _current._input_b.FindMaxSize(ref width, ref height);
+ _current._input_a.FindMaxSize(ref width, ref height);
+
+ RenderTexture target = new RenderTexture(width,height, 24, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
+ target.enableRandomWrite = true;
+ target.filterMode = GetFiltermode();
+ target.Create();
+
+ ComputeShader computeShader = AssetDatabase.FindAssets("ThryTexturePacker t:computeshader").
+ Select(g => AssetDatabase.GUIDToAssetPath(g)).Select(p => AssetDatabase.LoadAssetAtPath<ComputeShader>(p)).First();
+
+ computeShader.SetTexture(0, "Result", target);
+ computeShader.SetFloat("Width", width);
+ computeShader.SetFloat("Height", height);
+ computeShader.SetBool("TakeRGBFromRTexture", _firstTextureIsRGB);
+
+ _current._input_r.SetComputeShaderValues(computeShader, "R", width, height);
+ _current._input_g.SetComputeShaderValues(computeShader, "G", width, height);
+ _current._input_b.SetComputeShaderValues(computeShader, "B", width, height);
+ _current._input_a.SetComputeShaderValues(computeShader, "A", width, height);
+
+ computeShader.Dispatch(0, width / 8 + 1, height / 8 + 1, 1);
+
+ Texture2D atlas = new Texture2D(width, height, TextureFormat.RGBA32, true, !_makeSRGB);
+ RenderTexture.active = target;
+ atlas.ReadPixels(new Rect(0, 0, width, height), 0, 0);
+ atlas.Apply();
+
+ _current._packedTexture = atlas;
+ _prop.textureValue = _current._packedTexture;
+
+ _current._hasTextureChanged = true;
+ }
+
+ FilterMode GetFiltermode()
+ {
+ if (_current._input_r.Texture != null) return _current._input_r.Texture.filterMode;
+ if (_current._input_g.Texture != null) return _current._input_g.Texture.filterMode;
+ if (_current._input_b.Texture != null) return _current._input_b.Texture.filterMode;
+ if (_current._input_a.Texture != null) return _current._input_a.Texture.filterMode;
+ return FilterMode.Bilinear;
+ }
+
+ void Confirm()
+ {
+ if (_current._packedTexture == null) Pack();
+ string path = System.IO.Path.GetDirectoryName(AssetDatabase.GetAssetPath(ShaderEditor.Active.Materials[0]));
+ path = path + "/" + ShaderEditor.Active.Materials[0].name + _prop.name + ".png";
+ _prop.textureValue = TextureHelper.SaveTextureAsPNG(_current._packedTexture, path);
+ TextureImporter importer = AssetImporter.GetAtPath(path) as TextureImporter;
+ importer.streamingMipmaps = true;
+ importer.crunchedCompression = true;
+ importer.sRGBTexture = _makeSRGB;
+ importer.filterMode = GetFiltermode();
+ importer.SaveAndReimport();
+
+ _current._hasConfigChanged = false;
+ _current._hasTextureChanged = false;
+ _current._lastConfirmTime = Helper.DatetimeToUnixSeconds(DateTime.Now);
+ }
+
+ void Revert()
+ {
+ _prop.textureValue = _current._previousTexture;
+
+ _current._hasTextureChanged = false;
+ }
+
+ public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
+ {
+ DrawingData.LastPropertyUsedCustomDrawer = true;
+ return base.GetPropertyHeight(prop, label, editor);
+ }
+
+ class PackerChannelConfig
+ {
+ public Texture2D Texture;
+ public bool Invert;
+ public float Fallback;
+ public TextureChannel Channel = TextureChannel.Max;
+
+ Texture2D _loadedUnityTexture;
+ Texture2D _loadedUncompressedTexture;
+ public long LastHandledTextureEditTime;
+ public bool DoReloadUncompressedTexture;
+
+ public void FindMaxSize(ref int width, ref int height)
+ {
+ if (Texture == null) return;
+ if (_loadedUnityTexture != Texture || _loadedUncompressedTexture == null || DoReloadUncompressedTexture)
+ {
+ string path = AssetDatabase.GetAssetPath(Texture);
+ if(path.EndsWith(".png") || path.EndsWith(".jpg"))
+ {
+ _loadedUncompressedTexture = new Texture2D(Texture.width, Texture.height, TextureFormat.ARGB32, false, true);
+ ImageConversion.LoadImage(_loadedUncompressedTexture, System.IO.File.ReadAllBytes(path));
+ }else if (path.EndsWith(".tga"))
+ {
+ _loadedUncompressedTexture = TextureHelper.LoadTGA(path);
+ }
+ else
+ {
+ _loadedUncompressedTexture = Texture;
+ }
+ }
+ _loadedUnityTexture = Texture;
+ width = Mathf.Max(width, _loadedUncompressedTexture.width);
+ height = Mathf.Max(height, _loadedUncompressedTexture.height);
+ }
+
+ public void SetComputeShaderValues(ComputeShader computeShader, string prefix, int maxWidth, int maxHeight)
+ {
+ //Always setting texture cause else null error, cant branch in shader (executes both sides always)
+ if(Texture == null) computeShader.SetTexture(0, prefix + "_Input", Texture2D.whiteTexture);
+ else computeShader.SetTexture(0, prefix + "_Input", _loadedUncompressedTexture);
+ computeShader.SetVector(prefix+"_Config", GetComputeShaderConfig(maxWidth, maxHeight));
+ }
+
+ public Vector4 GetComputeShaderConfig(int maxWidth, int maxHeight)
+ {
+ float hasTexture = Texture != null ? 1 : 0;
+ float billinearFiltering = (Texture != null && (maxWidth != _loadedUncompressedTexture.width || maxHeight != _loadedUncompressedTexture.height)) ? 1 : 0;
+ return new Vector4(hasTexture, Texture == null?Fallback: billinearFiltering, (int)Channel, Invert ? 1 : 0);
+ }
+ }
+ enum TextureChannel { R, G, B, A, Max }
+ }
+
+ public class GradientDrawer : MaterialPropertyDrawer
+ {
+ GradientData data;
+ bool is_init = false;
+
+ Rect border_position;
+ Rect gradient_position;
+
+ private void Init(MaterialProperty prop)
+ {
+ data = new GradientData();
+ data.PreviewTexture = prop.textureValue;
+ is_init = true;
+ }
+
+ public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
+ {
+ if (!is_init)
+ Init(prop);
+
+ EditorGUI.BeginChangeCheck();
+ if (EditorGUI.EndChangeCheck())
+ Init(prop);
+
+ UpdateRects(position);
+ if (ShaderEditor.Input.Click && border_position.Contains(Event.current.mousePosition))
+ {
+ ShaderEditor.Input.Use();
+ PropertyOptions options = ShaderEditor.Active.CurrentProperty.Options;
+ GradientEditor.Open(data, prop, options.texture, options.force_texture_options, !options.force_texture_options);
+ }
+
+ GuiHelper.SmallTextureProperty(position, prop, label, editor, DrawingData.CurrentTextureProperty.hasFoldoutProperties);
+
+ GradientField();
+ }
+
+ private void UpdateRects(Rect position)
+ {
+ border_position = new Rect(position.x + EditorGUIUtility.labelWidth, position.y, position.width - EditorGUIUtility.labelWidth - position.x, position.height);
+ gradient_position = new Rect(border_position.x + 1, border_position.y + 1, border_position.width - 2, border_position.height - 2);
+ }
+
+ private void GradientField()
+ {
+ DrawBackgroundTexture();
+ if (data.PreviewTexture != null)
+ DrawGradientTexture();
+ else
+ GUI.DrawTexture(border_position, Texture2D.whiteTexture, ScaleMode.StretchToFill, false, 0, Color.grey, 1, 1);
+ }
+
+ private void DrawBackgroundTexture()
+ {
+ Texture2D backgroundTexture = TextureHelper.GetBackgroundTexture();
+ Rect texCoordsRect = new Rect(0, 0, gradient_position.width / backgroundTexture.width, gradient_position.height / backgroundTexture.height);
+ GUI.DrawTextureWithTexCoords(gradient_position, backgroundTexture, texCoordsRect, false);
+ }
+
+ private void DrawGradientTexture()
+ {
+ TextureWrapMode wrap_mode = data.PreviewTexture.wrapMode;
+ data.PreviewTexture.wrapMode = TextureWrapMode.Clamp;
+ bool vertical = data.PreviewTexture.height > data.PreviewTexture.width;
+ Vector2 pivot = new Vector2();
+ if (vertical)
+ {
+ pivot = new Vector2(gradient_position.x, gradient_position.y + gradient_position.height);
+ GUIUtility.RotateAroundPivot(-90, pivot);
+ gradient_position.y += gradient_position.height;
+ float h = gradient_position.width;
+ gradient_position.width = gradient_position.height;
+ gradient_position.y += h;
+ gradient_position.height = -h;
+ }
+ GUI.DrawTexture(gradient_position, data.PreviewTexture, ScaleMode.StretchToFill, true);
+ if (vertical)
+ {
+ GUIUtility.RotateAroundPivot(90, pivot);
+ }
+ GUI.DrawTexture(border_position, data.PreviewTexture, ScaleMode.StretchToFill, false, 0, Color.grey, 1, 1);
+ data.PreviewTexture.wrapMode = wrap_mode;
+ }
+
+ public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
+ {
+ DrawingData.LastPropertyUsedCustomDrawer = true;
+ return base.GetPropertyHeight(prop, label, editor);
+ }
+ }
+
+ public class TextureArrayDrawer : MaterialPropertyDrawer
+ {
+ private string framesProperty;
+
+ public TextureArrayDrawer(){}
+
+ public TextureArrayDrawer(string framesProperty)
+ {
+ this.framesProperty = framesProperty;
+ }
+
+ public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
+ {
+ ShaderProperty shaderProperty = (ShaderProperty)ShaderEditor.Active.CurrentProperty;
+ GuiHelper.ConfigTextureProperty(position, prop, label, editor, true, true);
+
+ if ((ShaderEditor.Input.is_drag_drop_event) && position.Contains(ShaderEditor.Input.mouse_position))
+ {
+ DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
+ if (ShaderEditor.Input.is_drop_event)
+ {
+ DragAndDrop.AcceptDrag();
+ HanldeDropEvent(prop, shaderProperty);
+ }
+ }
+ if (ShaderEditor.Active.IsFirstCall)
+ ShaderEditor.Active.TextureArrayProperties.Add(shaderProperty);
+ }
+
+ public void HanldeDropEvent(MaterialProperty prop, ShaderProperty shaderProperty)
+ {
+ string[] paths = DragAndDrop.paths;
+ Texture2DArray tex;
+ if (AssetDatabase.GetMainAssetTypeAtPath(paths[0]) != typeof(Texture2DArray))
+ tex = Converter.PathsToTexture2DArray(paths);
+ else
+ tex = AssetDatabase.LoadAssetAtPath<Texture2DArray>(paths[0]);
+ prop.textureValue = tex;
+ UpdateFramesProperty(prop, shaderProperty, tex);
+ EditorGUIUtility.ExitGUI();
+ }
+
+ private void UpdateFramesProperty(MaterialProperty prop, ShaderProperty shaderProperty, Texture2DArray tex)
+ {
+ if (framesProperty == null)
+ framesProperty = shaderProperty.Options.reference_property;
+
+ if (framesProperty != null)
+ {
+ if (ShaderEditor.Active.PropertyDictionary.ContainsKey(framesProperty))
+ ShaderEditor.Active.PropertyDictionary[framesProperty].MaterialProperty.floatValue = tex.depth;
+ }
+ }
+
+ public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
+ {
+ DrawingData.LastPropertyUsedCustomDrawer = true;
+ return base.GetPropertyHeight(prop, label, editor);
+ }
+ }
+#endregion
+
+ #region Decorators
+ public class ThryHeaderLabelDecorator : MaterialPropertyDrawer
+ {
+ readonly string text;
+ readonly int size;
+ GUIStyle style;
+
+ public ThryHeaderLabelDecorator(string text) : this(text, EditorStyles.standardFont.fontSize)
+ {
+ }
+ public ThryHeaderLabelDecorator(string text, float size)
+ {
+ this.text = text;
+ this.size = (int)size;
+ style = new GUIStyle(EditorStyles.boldLabel);
+ style.fontSize = this.size;
+ }
+
+
+ public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
+ {
+ return size + 6;
+ }
+
+ public override void OnGUI(Rect position, MaterialProperty prop, string label, MaterialEditor editor)
+ {
+ float offst = position.height;
+ position = EditorGUI.IndentedRect(position);
+ GUI.Label(position, text, style);
+ }
+ }
+
+ public class ThryRichLabelDrawer : MaterialPropertyDrawer
+ {
+ readonly int size;
+ GUIStyle style;
+
+ public ThryRichLabelDrawer(float size)
+ {
+ this.size = (int)size;
+ style = new GUIStyle(EditorStyles.boldLabel);
+ style.richText = true;
+ style.fontSize = this.size;
+ }
+
+ public ThryRichLabelDrawer() : this(EditorStyles.standardFont.fontSize) {}
+
+ public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
+ {
+ return size + 4;
+ }
+
+ public override void OnGUI(Rect position, MaterialProperty prop, string label, MaterialEditor editor)
+ {
+ float offst = position.height;
+ position = EditorGUI.IndentedRect(position);
+ GUI.Label(position, label, style);
+ }
+ }
+#endregion
+
+ public class ThryToggleDrawer : MaterialPropertyDrawer
+ {
+ public string keyword;
+ private bool isFirstGUICall = true;
+ public bool left = false;
+ private bool hasKeyword = false;
+
+ public ThryToggleDrawer()
+ {
+ }
+
+ //the reason for weird string thing here is that you cant have bools as params for drawers
+ public ThryToggleDrawer(string keywordLeft)
+ {
+ if (keywordLeft == "true") left = true;
+ else if (keywordLeft == "false") left = false;
+ else keyword = keywordLeft;
+ hasKeyword = keyword != null;
+ }
+
+ public ThryToggleDrawer(string keyword, string left)
+ {
+ this.keyword = keyword;
+ this.left = left == "true";
+ hasKeyword = keyword != null;
+ }
+
+ protected void SetKeyword(MaterialProperty prop, bool on)
+ {
+ SetKeywordInternal(prop, on, "_ON");
+ }
+
+ protected void CheckKeyword(MaterialProperty prop)
+ {
+ if (prop.hasMixedValue)
+ {
+ foreach (Material m in prop.targets)
+ {
+ if (m.GetFloat(prop.name) == 1)
+ m.EnableKeyword(keyword);
+ else
+ m.DisableKeyword(keyword);
+ }
+ }
+ else
+ {
+ foreach (Material m in prop.targets)
+ {
+ if (prop.floatValue == 1)
+ m.EnableKeyword(keyword);
+ else
+ m.DisableKeyword(keyword);
+ }
+ }
+ }
+
+ static bool IsPropertyTypeSuitable(MaterialProperty prop)
+ {
+ return prop.type == MaterialProperty.PropType.Float || prop.type == MaterialProperty.PropType.Range;
+ }
+
+ public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
+ {
+ if (!IsPropertyTypeSuitable(prop))
+ {
+ return EditorGUIUtility.singleLineHeight * 2.5f;
+ }
+ if (hasKeyword) CheckKeyword(prop);
+ return base.GetPropertyHeight(prop, label, editor);
+ }
+
+ public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
+ {
+ if (!IsPropertyTypeSuitable(prop))
+ {
+ return;
+ }
+ if (isFirstGUICall && !ShaderEditor.Active.IsLockedMaterial)
+ {
+ if(hasKeyword) CheckKeyword(prop);
+ isFirstGUICall = false;
+ }
+ //why is this not inFirstGUICall ? cause it seems drawers are kept between different openings of the shader editor, so this needs to be set again every time the shader editor is reopened for that material
+ (ShaderEditor.Active.PropertyDictionary[prop.name] as ShaderProperty).keyword = keyword;
+
+ EditorGUI.BeginChangeCheck();
+
+ bool value = (Math.Abs(prop.floatValue) > 0.001f);
+ EditorGUI.showMixedValue = prop.hasMixedValue;
+ if(left) value = EditorGUI.ToggleLeft(position, label, value, Styles.style_toggle_left_richtext);
+ else value = EditorGUI.Toggle(position, label, value);
+ EditorGUI.showMixedValue = false;
+ if (EditorGUI.EndChangeCheck())
+ {
+ prop.floatValue = value ? 1.0f : 0.0f;
+ if(hasKeyword) SetKeyword(prop, value);
+ }
+ }
+
+ public override void Apply(MaterialProperty prop)
+ {
+ base.Apply(prop);
+ if (!IsPropertyTypeSuitable(prop))
+ return;
+
+ if (prop.hasMixedValue)
+ return;
+
+ if(hasKeyword) SetKeyword(prop, (Math.Abs(prop.floatValue) > 0.001f));
+ }
+
+ protected void SetKeywordInternal(MaterialProperty prop, bool on, string defaultKeywordSuffix)
+ {
+ // if no keyword is provided, use <uppercase property name> + defaultKeywordSuffix
+ string kw = string.IsNullOrEmpty(keyword) ? prop.name.ToUpperInvariant() + defaultKeywordSuffix : keyword;
+ // set or clear the keyword
+ foreach (Material material in prop.targets)
+ {
+ if (on)
+ material.EnableKeyword(kw);
+ else
+ material.DisableKeyword(kw);
+ }
+ }
+ }
+
+ //This class only exists for backward compatibility
+ public class ThryToggleUIDrawer: ThryToggleDrawer
+ {
+ public ThryToggleUIDrawer()
+ {
+ }
+
+ //the reason for weird string thing here is that you cant have bools as params for drawers
+ public ThryToggleUIDrawer(string keywordLeft)
+ {
+ if (keywordLeft == "true") left = true;
+ else if (keywordLeft == "false") left = false;
+ else keyword = keywordLeft;
+ }
+
+ public ThryToggleUIDrawer(string keyword, string left)
+ {
+ this.keyword = keyword;
+ this.left = left == "true";
+ }
+ }
+
+ public class MultiSliderDrawer : MaterialPropertyDrawer
+ {
+ public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
+ {
+ GuiHelper.MinMaxSlider(position, label, prop);
+ }
+
+ public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
+ {
+ DrawingData.LastPropertyUsedCustomDrawer = true;
+ return base.GetPropertyHeight(prop, label, editor);
+ }
+ }
+
+ public class VectorToSlidersDrawer : MaterialPropertyDrawer
+ {
+ class SliderConfig
+ {
+ public string Label;
+ public float Min;
+ public float Max;
+
+ public SliderConfig(string l, float min, float max)
+ {
+ Label = l;
+ Min = min;
+ Max = max;
+ }
+ }
+
+ SliderConfig _slider1;
+ SliderConfig _slider2;
+ SliderConfig _slider3;
+ SliderConfig _slider4;
+ bool _twoMinMaxDrawers;
+
+ VectorToSlidersDrawer(SliderConfig slider1, SliderConfig slider2, SliderConfig slider3, SliderConfig slider4, float twoMinMaxDrawers)
+ {
+ _slider1 = slider1;
+ _slider2 = slider2;
+ _slider3 = slider3;
+ _slider4 = slider4;
+ _twoMinMaxDrawers = twoMinMaxDrawers == 1;
+ }
+
+ public VectorToSlidersDrawer(string label1, float min1, float max1, string label2, float min2, float max2, string label3, float min3, float max3, string label4, float min4, float max4) :
+ this(new SliderConfig(label1, min1, max1), new SliderConfig(label2, min2, max2), new SliderConfig(label3, min3, max3), new SliderConfig(label4, min4, max4), 0) { }
+ public VectorToSlidersDrawer(string label1, float min1, float max1, string label2, float min2, float max2, string label3, float min3, float max3) :
+ this(new SliderConfig(label1, min1, max1), new SliderConfig(label2, min2, max2), new SliderConfig(label3, min3, max3), null, 0){ }
+ public VectorToSlidersDrawer(string label1, float min1, float max1, string label2, float min2, float max2) :
+ this(new SliderConfig(label1, min1, max1), new SliderConfig(label2, min2, max2), null, null, 0){ }
+ public VectorToSlidersDrawer(float twoMinMaxDrawers, string label1, float min1, float max1, string label2, float min2, float max2) :
+ this(new SliderConfig(label1, min1, max1), new SliderConfig(label2, min2, max2), null, null, twoMinMaxDrawers){ }
+
+ public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
+ {
+ Vector4 vector = prop.vectorValue;
+ EditorGUI.BeginChangeCheck();
+ if (_twoMinMaxDrawers)
+ {
+ float min1 = vector.x;
+ float max1 = vector.y;
+ float min2 = vector.z;
+ float max2 = vector.w;
+ EditorGUI.showMixedValue = prop.hasMixedValue;
+ EditorGUILayout.MinMaxSlider(_slider1.Label, ref min1, ref max1, _slider1.Min, _slider1.Max);
+ EditorGUI.showMixedValue = prop.hasMixedValue;
+ EditorGUILayout.MinMaxSlider(_slider2.Label, ref min2, ref max2, _slider2.Min, _slider2.Max);
+ vector = new Vector4(min1, max1, min2, max2);
+ }
+ else
+ {
+ EditorGUI.showMixedValue = prop.hasMixedValue;
+ vector.x = EditorGUILayout.Slider(_slider1.Label, vector.x, _slider1.Min, _slider1.Max);
+ EditorGUI.showMixedValue = prop.hasMixedValue;
+ vector.y = EditorGUILayout.Slider(_slider2.Label, vector.y, _slider2.Min, _slider2.Max);
+ if (_slider3 != null)
+ {
+ EditorGUI.showMixedValue = prop.hasMixedValue;
+ vector.z = EditorGUILayout.Slider(_slider3.Label, vector.z, _slider3.Min, _slider3.Max);
+ }
+ if(_slider4 != null)
+ {
+ EditorGUI.showMixedValue = prop.hasMixedValue;
+ vector.w = EditorGUILayout.Slider(_slider4.Label, vector.w, _slider4.Min, _slider4.Max);
+ }
+ }
+ if(EditorGUI.EndChangeCheck())
+ prop.vectorValue = vector;
+ }
+
+ public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
+ {
+ DrawingData.LastPropertyUsedCustomDrawer = true;
+ return base.GetPropertyHeight(prop, label, editor) - EditorGUIUtility.singleLineHeight;
+ }
+ }
+
+ public class Vector4TogglesDrawer : MaterialPropertyDrawer
+ {
+ public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
+ {
+ EditorGUI.BeginChangeCheck();
+ EditorGUI.showMixedValue = prop.hasMixedValue;
+ EditorGUI.LabelField(position, label);
+ position.x += EditorGUIUtility.labelWidth;
+ position.width = (position.width - EditorGUIUtility.labelWidth) / 4;
+ bool b1 = GUI.Toggle(position, prop.vectorValue.x == 1, "");
+ position.x += position.width;
+ bool b2 = GUI.Toggle(position, prop.vectorValue.y == 1, "");
+ position.x += position.width;
+ bool b3 = GUI.Toggle(position, prop.vectorValue.z == 1, "");
+ position.x += position.width;
+ bool b4 = GUI.Toggle(position, prop.vectorValue.w == 1, "");
+ if (EditorGUI.EndChangeCheck())
+ {
+ prop.vectorValue = new Vector4(b1 ? 1 : 0, b2 ? 1 : 0, b3 ? 1 : 0, b4 ? 1 : 0);
+ }
+ }
+
+ public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
+ {
+ DrawingData.LastPropertyUsedCustomDrawer = true;
+ return base.GetPropertyHeight(prop, label, editor);
+ }
+ }
+
+ public class ThryMultiFloatsDrawer : MaterialPropertyDrawer
+ {
+ string[] _otherProperties;
+ bool _displayAsToggles;
+
+ public ThryMultiFloatsDrawer(string displayAsToggles, string p1, string p2, string p3, string p4, string p5, string p6, string p7) : this(displayAsToggles, new string[] { p1, p2, p3, p4, p5, p6, p7 }) { }
+ public ThryMultiFloatsDrawer(string displayAsToggles, string p1, string p2, string p3, string p4, string p5, string p6) : this(displayAsToggles, new string[] { p1, p2, p3, p4, p5, p6 }) { }
+ public ThryMultiFloatsDrawer(string displayAsToggles, string p1, string p2, string p3, string p4, string p5) : this(displayAsToggles, new string[] { p1, p2, p3, p4, p5 }) { }
+ public ThryMultiFloatsDrawer(string displayAsToggles, string p1, string p2, string p3, string p4) : this(displayAsToggles, new string[] { p1, p2, p3, p4 }) { }
+ public ThryMultiFloatsDrawer(string displayAsToggles, string p1, string p2, string p3) : this(displayAsToggles, new string[] { p1, p2, p3 }) { }
+ public ThryMultiFloatsDrawer(string displayAsToggles, string p1, string p2) : this(displayAsToggles, new string[] { p1, p2 }) { }
+ public ThryMultiFloatsDrawer(string displayAsToggles, string p1) : this(displayAsToggles, new string[] { p1 }) { }
+
+ public ThryMultiFloatsDrawer(string displayAsToggles, params string[] extraProperties)
+ {
+ _displayAsToggles = displayAsToggles.ToLower() == "true" || displayAsToggles == "1";
+ _otherProperties = extraProperties;
+ }
+
+ public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
+ {
+ Rect labelR = new Rect(position);
+ labelR.width = EditorGUIUtility.labelWidth;
+ Rect contentR = new Rect(position);
+ contentR.width = (contentR.width - labelR.width) / (_otherProperties.Length + 1);
+ contentR.x += labelR.width;
+
+ EditorGUI.LabelField(labelR, label);
+ int indentLevel = EditorGUI.indentLevel; //else it double indents
+ EditorGUI.indentLevel = 0;
+ PropGUI(prop, contentR, 0);
+ for (int i = 0; i < _otherProperties.Length; i++)
+ PropGUI(ShaderEditor.Active.PropertyDictionary[_otherProperties[i]].MaterialProperty, contentR, i + 1);
+ EditorGUI.indentLevel = indentLevel;
+
+ //make sure all are animated together
+ bool animated = ShaderEditor.Active.CurrentProperty.IsAnimated;
+ bool renamed = ShaderEditor.Active.CurrentProperty.IsRenaming;
+ for (int i = 0; i < _otherProperties.Length; i++)
+ ShaderEditor.Active.PropertyDictionary[_otherProperties[i]].SetAnimated(animated, renamed);
+ }
+
+ void PropGUI(MaterialProperty prop, Rect contentRect, int index)
+ {
+ contentRect.x += contentRect.width * index;
+ contentRect.width -= 5;
+ if (_displayAsToggles) prop.floatValue = EditorGUI.Toggle(contentRect, prop.floatValue == 1) ? 1 : 0;
+ else prop.floatValue = EditorGUI.FloatField(contentRect, prop.floatValue);
+ }
+
+ public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
+ {
+ DrawingData.LastPropertyUsedCustomDrawer = true;
+ return base.GetPropertyHeight(prop, label, editor);
+ }
+ }
+
+ public class Vector3Drawer : MaterialPropertyDrawer
+ {
+ public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
+ {
+ EditorGUI.BeginChangeCheck();
+ EditorGUI.showMixedValue = prop.hasMixedValue;
+ Vector4 vec = EditorGUI.Vector3Field(position, label, prop.vectorValue);
+ if (EditorGUI.EndChangeCheck())
+ {
+ prop.vectorValue = new Vector4(vec.x, vec.y, vec.z, prop.vectorValue.w);
+ }
+ }
+
+ public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
+ {
+ DrawingData.LastPropertyUsedCustomDrawer = true;
+ return base.GetPropertyHeight(prop, label, editor);
+ }
+ }
+
+ public class Vector2Drawer : MaterialPropertyDrawer
+ {
+ public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
+ {
+ EditorGUI.BeginChangeCheck();
+ EditorGUI.showMixedValue = prop.hasMixedValue;
+ Vector4 vec = EditorGUI.Vector2Field(position, label, prop.vectorValue);
+ if (EditorGUI.EndChangeCheck())
+ {
+ prop.vectorValue = new Vector4(vec.x, vec.y, prop.vectorValue.z, prop.vectorValue.w);
+ }
+ }
+
+ public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
+ {
+ DrawingData.LastPropertyUsedCustomDrawer = true;
+ return base.GetPropertyHeight(prop, label, editor);
+ }
+ }
+
+ public class HelpboxDrawer : MaterialPropertyDrawer
+ {
+ readonly MessageType type;
+
+ public HelpboxDrawer()
+ {
+ type = MessageType.Info;
+ }
+
+ public HelpboxDrawer(float f)
+ {
+ type = (MessageType)(int)f;
+ }
+
+ public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
+ {
+ EditorGUILayout.HelpBox(label.text, type);
+ }
+
+ public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
+ {
+ DrawingData.LastPropertyUsedCustomDrawer = true;
+ return 0;
+ }
+ }
+
+ public class sRGBWarningDrawer : MaterialPropertyDrawer
+ {
+ public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
+ {
+ GuiHelper.ConfigTextureProperty(position, prop, label, editor, ((TextureProperty)ShaderEditor.Active.CurrentProperty).hasScaleOffset);
+ GuiHelper.sRGBWarning(prop);
+ }
+
+ public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
+ {
+ DrawingData.LastPropertyUsedCustomDrawer = true;
+ return base.GetPropertyHeight(prop, label, editor);
+ }
+ }
+
+ public enum ColorMask
+ {
+ None,
+ Alpha,
+ Blue,
+ BA,
+ Green,
+ GA,
+ GB,
+ GBA,
+ Red,
+ RA,
+ RB,
+ RBA,
+ RG,
+ RGA,
+ RGB,
+ RGBA
+ }
+
+ // DX11 only blend operations
+ public enum BlendOp
+ {
+ Add,
+ Subtract,
+ ReverseSubtract,
+ Min,
+ Max,
+ LogicalClear,
+ LogicalSet,
+ LogicalCopy,
+ LogicalCopyInverted,
+ LogicalNoop,
+ LogicalInvert,
+ LogicalAnd,
+ LogicalNand,
+ LogicalOr,
+ LogicalNor,
+ LogicalXor,
+ LogicalEquivalence,
+ LogicalAndReverse,
+ LogicalAndInverted,
+ LogicalOrReverse,
+ LogicalOrInverted
+ }
+
+ //Original Code from https://github.com/DarthShader/Kaj-Unity-Shaders
+ /**MIT License
+
+ Copyright (c) 2020 DarthShader
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.**/
+ public class ThryShaderOptimizerLockButtonDrawer : MaterialPropertyDrawer
+ {
+ public override void OnGUI(Rect position, MaterialProperty shaderOptimizer, string label, MaterialEditor materialEditor)
+ {
+ bool isLocked = (shaderOptimizer.targets[0] as Material).shader.name.StartsWith("Hidden/") && (shaderOptimizer.targets[0] as Material).GetTag("OriginalShader",false,"") != "";
+ //this will make sure the button is unlocked if you manually swap to an unlocked shader
+ //shaders that have the ability to be locked shouldnt really be hidden themself. at least it wouldnt make too much sense
+ if (shaderOptimizer.hasMixedValue == false && shaderOptimizer.floatValue == 1 && isLocked == false)
+ {
+ shaderOptimizer.floatValue = 0;
+ }else if(shaderOptimizer.hasMixedValue == false && shaderOptimizer.floatValue == 0 && isLocked)
+ {
+ shaderOptimizer.floatValue = 1;
+ }
+
+ // Theoretically this shouldn't ever happen since locked in materials have different shaders.
+ // But in a case where the material property says its locked in but the material really isn't, this
+ // will display and allow users to fix the property/lock in
+ ShaderEditor.Active.IsLockedMaterial = shaderOptimizer.floatValue == 1;
+ if (shaderOptimizer.hasMixedValue)
+ {
+ EditorGUI.BeginChangeCheck();
+ GUILayout.Button(Locale.editor.Get("lockin_button_multi").ReplaceVariables(materialEditor.targets.Length));
+ if (EditorGUI.EndChangeCheck())
+ {
+ SaveChangeStack();
+ Material[] materials = new Material[shaderOptimizer.targets.Length];
+ for (int i = 0; i < materials.Length; i++) materials[i] = shaderOptimizer.targets[i] as Material;
+ ShaderOptimizer.SetLockedForAllMaterials(materials, shaderOptimizer.floatValue == 1 ? 0 : 1, true, false, false, shaderOptimizer);
+ RestoreChangeStack();
+ }
+ }
+ else
+ {
+ EditorGUI.BeginChangeCheck();
+ if (shaderOptimizer.floatValue == 0)
+ {
+ if (materialEditor.targets.Length == 1)
+ GUILayout.Button(Locale.editor.Get("lockin_button_single"));
+ else GUILayout.Button(Locale.editor.Get("lockin_button_multi").ReplaceVariables(materialEditor.targets.Length));
+ }
+ else
+ {
+ if (materialEditor.targets.Length == 1)
+ GUILayout.Button(Locale.editor.Get("unlock_button_single"));
+ else GUILayout.Button(Locale.editor.Get("unlock_button_multi").ReplaceVariables(materialEditor.targets.Length));
+ }
+ if (EditorGUI.EndChangeCheck())
+ {
+ SaveChangeStack();
+ Material[] materials = new Material[shaderOptimizer.targets.Length];
+ for (int i = 0; i < materials.Length; i++) materials[i] = shaderOptimizer.targets[i] as Material;
+ ShaderOptimizer.SetLockedForAllMaterials(materials, shaderOptimizer.floatValue == 1 ? 0 : 1, true, false, false, shaderOptimizer);
+ RestoreChangeStack();
+ }
+ }
+ if(Config.Singleton.allowCustomLockingRenaming && !ShaderEditor.Active.IsLockedMaterial)
+ {
+ EditorGUI.BeginChangeCheck();
+ ShaderEditor.Active.RenamedPropertySuffix = EditorGUILayout.TextField("Locked property suffix: ", ShaderEditor.Active.RenamedPropertySuffix);
+ if (EditorGUI.EndChangeCheck())
+ {
+ foreach (Material m in ShaderEditor.Active.Materials)
+ m.SetOverrideTag("thry_rename_suffix", ShaderEditor.Active.RenamedPropertySuffix);
+ if (ShaderEditor.Active.RenamedPropertySuffix == "")
+ ShaderEditor.Active.RenamedPropertySuffix = ShaderOptimizer.GetRenamedPropertySuffix(ShaderEditor.Active.Materials[0]);
+ }
+ }
+ }
+
+ //This code purly exists cause Unity 2019 is a piece of shit that looses it's internal change stack on locking CAUSE FUCK IF I KNOW
+ static System.Reflection.FieldInfo changeStack = typeof(EditorGUI).GetField("s_ChangedStack", BindingFlags.Static | BindingFlags.NonPublic);
+ static int preLockStackSize = 0;
+ private static void SaveChangeStack()
+ {
+ if (changeStack != null)
+ {
+ Stack<bool> stack = (Stack<bool>)changeStack.GetValue(null);
+ if(stack != null)
+ {
+ preLockStackSize = stack.Count();
+ }
+ }
+ }
+
+ private static void RestoreChangeStack()
+ {
+ if (changeStack != null)
+ {
+ Stack<bool> stack = (Stack<bool>)changeStack.GetValue(null);
+ if (stack != null)
+ {
+ int postLockStackSize = stack.Count();
+ //Restore change stack from before lock / unlocking
+ for(int i=postLockStackSize; i < preLockStackSize; i++)
+ {
+ EditorGUI.BeginChangeCheck();
+ }
+ }
+ }
+ }
+
+ public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
+ {
+ DrawingData.LastPropertyUsedCustomDrawer = true;
+ DrawingData.LastPropertyDoesntAllowAnimation = true;
+ ShaderEditor.Active.DoUseShaderOptimizer = true;
+ return -2;
+ }
+ }
+
+ // Enum with normal editor width, rather than MaterialEditor Default GUI widths
+ // Would be nice if Decorators could access Drawers too so this wouldn't be necessary for something to trivial
+ // Adapted from Unity interal MaterialEnumDrawer https://github.com/Unity-Technologies/UnityCsReference/
+ public class ThryWideEnumDrawer : MaterialPropertyDrawer
+ {
+ private readonly GUIContent[] names;
+ private readonly float[] values;
+
+ // internal Unity AssemblyHelper can't be accessed
+ private Type[] TypesFromAssembly(Assembly a)
+ {
+ if (a == null)
+ return new Type[0];
+ try
+ {
+ return a.GetTypes();
+ }
+ catch (ReflectionTypeLoadException)
+ {
+ return new Type[0];
+ }
+ }
+ public ThryWideEnumDrawer(string enumName,int j)
+ {
+ var types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(
+ x => TypesFromAssembly(x)).ToArray();
+ try
+ {
+ var enumType = types.FirstOrDefault(
+ x => x.IsEnum && (x.Name == enumName || x.FullName == enumName)
+ );
+ var enumNames = Enum.GetNames(enumType);
+ names = new GUIContent[enumNames.Length];
+ for (int i = 0; i < enumNames.Length; ++i)
+ names[i] = new GUIContent(enumNames[i]);
+
+ var enumVals = Enum.GetValues(enumType);
+ values = new float[enumVals.Length];
+ for (int i = 0; i < enumVals.Length; ++i)
+ values[i] = (int)enumVals.GetValue(i);
+ }
+ catch (Exception)
+ {
+ Debug.LogWarningFormat("Failed to create WideEnum, enum {0} not found", enumName);
+ throw;
+ }
+
+ }
+
+ public ThryWideEnumDrawer(string n1, float v1) : this(new[] { n1 }, new[] { v1 }) { }
+ public ThryWideEnumDrawer(string n1, float v1, string n2, float v2) : this(new[] { n1, n2 }, new[] { v1, v2 }) { }
+ public ThryWideEnumDrawer(string n1, float v1, string n2, float v2, string n3, float v3) : this(new[] { n1, n2, n3 }, new[] { v1, v2, v3 }) { }
+ public ThryWideEnumDrawer(string n1, float v1, string n2, float v2, string n3, float v3, string n4, float v4) : this(new[] { n1, n2, n3, n4 }, new[] { v1, v2, v3, v4 }) { }
+ public ThryWideEnumDrawer(string n1, float v1, string n2, float v2, string n3, float v3, string n4, float v4, string n5, float v5) : this(new[] { n1, n2, n3, n4, n5 }, new[] { v1, v2, v3, v4, v5 }) { }
+ public ThryWideEnumDrawer(string n1, float v1, string n2, float v2, string n3, float v3, string n4, float v4, string n5, float v5, string n6, float v6) : this(new[] { n1, n2, n3, n4, n5, n6 }, new[] { v1, v2, v3, v4, v5, v6 }) { }
+ public ThryWideEnumDrawer(string n1, float v1, string n2, float v2, string n3, float v3, string n4, float v4, string n5, float v5, string n6, float v6, string n7, float v7) : this(new[] { n1, n2, n3, n4, n5, n6, n7 }, new[] { v1, v2, v3, v4, v5, v6, v7 }) { }
+ public ThryWideEnumDrawer(string n1, float v1, string n2, float v2, string n3, float v3, string n4, float v4, string n5, float v5, string n6, float v6, string n7, float v7, string n8, float v8) : this(new[] { n1, n2, n3, n4, n5, n6, n7, n8}, new[] { v1, v2, v3, v4, v5, v6, v7, v8}) { }
+ public ThryWideEnumDrawer(string n1, float v1, string n2, float v2, string n3, float v3, string n4, float v4, string n5, float v5, string n6, float v6, string n7, float v7, string n8, float v8, string n9, float v9) : this(new[] { n1, n2, n3, n4, n5, n6, n7, n8, n9}, new[] { v1, v2, v3, v4, v5, v6, v7, v8, v9}) { }
+ public ThryWideEnumDrawer(string n1, float v1, string n2, float v2, string n3, float v3, string n4, float v4, string n5, float v5, string n6, float v6, string n7, float v7, string n8, float v8, string n9, float v9, string n10, float v10) : this(new[] { n1, n2, n3, n4, n5, n6, n7, n8, n9, n10 }, new[] { v1, v2, v3, v4, v5, v6, v7, v8, v9, v10 }) { }
+ public ThryWideEnumDrawer(string n1, float v1, string n2, float v2, string n3, float v3, string n4, float v4, string n5, float v5, string n6, float v6, string n7, float v7, string n8, float v8, string n9, float v9, string n10, float v10, string n11, float v11) : this(new[] { n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11 }, new[] { v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11 }) { }
+ public ThryWideEnumDrawer(string n1, float v1, string n2, float v2, string n3, float v3, string n4, float v4, string n5, float v5, string n6, float v6, string n7, float v7, string n8, float v8, string n9, float v9, string n10, float v10, string n11, float v11, string n12, float v12) : this(new[] { n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12 }, new[] { v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12 }) { }
+ public ThryWideEnumDrawer(string n1, float v1, string n2, float v2, string n3, float v3, string n4, float v4, string n5, float v5, string n6, float v6, string n7, float v7, string n8, float v8, string n9, float v9, string n10, float v10, string n11, float v11, string n12, float v12, string n13, float v13) : this(new[] { n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13 }, new[] { v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13 }) { }
+ public ThryWideEnumDrawer(string n1, float v1, string n2, float v2, string n3, float v3, string n4, float v4, string n5, float v5, string n6, float v6, string n7, float v7, string n8, float v8, string n9, float v9, string n10, float v10, string n11, float v11, string n12, float v12, string n13, float v13, string n14, float v14) : this(new[] { n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14 }, new[] { v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14 }) { }
+ public ThryWideEnumDrawer(string n1, float v1, string n2, float v2, string n3, float v3, string n4, float v4, string n5, float v5, string n6, float v6, string n7, float v7, string n8, float v8, string n9, float v9, string n10, float v10, string n11, float v11, string n12, float v12, string n13, float v13, string n14, float v14, string n15, float v15) : this(new[] { n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15 }, new[] { v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 }) { }
+ public ThryWideEnumDrawer(string n1, float v1, string n2, float v2, string n3, float v3, string n4, float v4, string n5, float v5, string n6, float v6, string n7, float v7, string n8, float v8, string n9, float v9, string n10, float v10, string n11, float v11, string n12, float v12, string n13, float v13, string n14, float v14, string n15, float v15, string n16, float v16) : this(new[] { n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16 }, new[] { v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16 }) { }
+ public ThryWideEnumDrawer(string n1, float v1, string n2, float v2, string n3, float v3, string n4, float v4, string n5, float v5, string n6, float v6, string n7, float v7, string n8, float v8, string n9, float v9, string n10, float v10, string n11, float v11, string n12, float v12, string n13, float v13, string n14, float v14, string n15, float v15, string n16, float v16, string n17, float v17) : this(new[] { n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17 }, new[] { v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17 }) { }
+ public ThryWideEnumDrawer(string n1, float v1, string n2, float v2, string n3, float v3, string n4, float v4, string n5, float v5, string n6, float v6, string n7, float v7, string n8, float v8, string n9, float v9, string n10, float v10, string n11, float v11, string n12, float v12, string n13, float v13, string n14, float v14, string n15, float v15, string n16, float v16, string n17, float v17, string n18, float v18) : this(new[] { n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17, n18 }, new[] { v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18 }) { }
+ public ThryWideEnumDrawer(string n1, float v1, string n2, float v2, string n3, float v3, string n4, float v4, string n5, float v5, string n6, float v6, string n7, float v7, string n8, float v8, string n9, float v9, string n10, float v10, string n11, float v11, string n12, float v12, string n13, float v13, string n14, float v14, string n15, float v15, string n16, float v16, string n17, float v17, string n18, float v18, string n19, float v19) : this(new[] { n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17, n18, n19 }, new[] { v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19 }) { }
+ public ThryWideEnumDrawer(string n1, float v1, string n2, float v2, string n3, float v3, string n4, float v4, string n5, float v5, string n6, float v6, string n7, float v7, string n8, float v8, string n9, float v9, string n10, float v10, string n11, float v11, string n12, float v12, string n13, float v13, string n14, float v14, string n15, float v15, string n16, float v16, string n17, float v17, string n18, float v18, string n19, float v19, string n20, float v20) : this(new[] { n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17, n18, n19, n20 }, new[] { v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20 }) { }
+ public ThryWideEnumDrawer(string[] enumNames, float[] vals)
+ {
+ names = new GUIContent[enumNames.Length];
+ for (int i = 0; i < enumNames.Length; ++i)
+ names[i] = new GUIContent(enumNames[i]);
+
+ values = new float[vals.Length];
+ for (int i = 0; i < vals.Length; ++i)
+ values[i] = vals[i];
+ }
+
+ public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
+ {
+ EditorGUI.showMixedValue = prop.hasMixedValue;
+ EditorGUI.BeginChangeCheck();
+ var value = prop.floatValue;
+ int selectedIndex = -1;
+ for (int i = 0; i < values.Length; i++)
+ if (values[i] == value)
+ {
+ selectedIndex = i;
+ break;
+ }
+
+ float labelWidth = EditorGUIUtility.labelWidth;
+ EditorGUIUtility.labelWidth = 0f;
+ var selIndex = EditorGUI.Popup(position, label, selectedIndex, names);
+ EditorGUI.showMixedValue = false;
+ if (EditorGUI.EndChangeCheck())
+ prop.floatValue = values[selIndex];
+ EditorGUIUtility.labelWidth = labelWidth;
+ }
+
+ public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
+ {
+ DrawingData.LastPropertyUsedCustomDrawer = true;
+ return base.GetPropertyHeight(prop, label, editor);
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Drawers.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Drawers.cs.meta
new file mode 100644
index 00000000..b1a60c1c
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Drawers.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f835e027ceb24bf4b8607004ce304fa7
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/EditorChanger.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/EditorChanger.cs
new file mode 100644
index 00000000..5ad9463a
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/EditorChanger.cs
@@ -0,0 +1,166 @@
+// Material/Shader Inspector for Unity 2017/2018
+// Copyright (C) 2019 Thryrallo
+
+using System.Collections;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+using UnityEditor;
+using UnityEngine;
+
+namespace Thry
+{
+ public class EditorChanger : EditorWindow
+ {
+ Vector2 scrollPos;
+
+ bool[] setEditor;
+ bool[] wasEditor;
+
+ List<string> paths = null;
+ List<Shader> shaders = null;
+
+ void OnGUI()
+ {
+ scrollPos = GUILayout.BeginScrollView(scrollPos);
+
+ bool init = false;
+
+ if (paths == null)
+ {
+ paths = new List<string>();
+ shaders = new List<Shader>();
+ string[] shaderGuids = AssetDatabase.FindAssets("t:shader");
+
+ for (int sguid = 0; sguid < shaderGuids.Length; sguid++)
+ {
+ string path = AssetDatabase.GUIDToAssetPath(shaderGuids[sguid]);
+ Shader shader = AssetDatabase.LoadAssetAtPath<Shader>(path);
+ paths.Add(path);
+ shaders.Add(shader);
+ }
+
+ if (setEditor == null || setEditor.Length != shaderGuids.Length)
+ {
+ setEditor = new bool[paths.Count];
+ wasEditor = new bool[paths.Count];
+ }
+ init = true;
+ }
+
+ for (int p = 0; p < paths.Count; p++)
+ {
+ if (init)
+ {
+ EditorUtility.DisplayProgressBar("Load all shaders...", "", (float)p / paths.Count);
+ setEditor[p] = ShaderHelper.IsShaderUsingShaderEditor(shaders[p]);
+ wasEditor[p] = setEditor[p];
+ }
+ setEditor[p] = GUILayout.Toggle(setEditor[p], shaders[p].name);
+ }
+ if (init) EditorUtility.ClearProgressBar();
+
+ GUILayout.EndScrollView();
+
+ if (GUILayout.Button("Apply"))
+ {
+ for (int i = 0; i < paths.Count; i++)
+ {
+ if (wasEditor[i] != setEditor[i])
+ {
+ string path = paths[i];
+ if (setEditor[i]) addShaderEditor(path);
+ else removeShaderEditor(path);
+ }
+
+ wasEditor[i] = setEditor[i];
+ }
+ AssetDatabase.Refresh();
+ ShaderEditor.RepaintActive();
+ }
+ }
+
+ private void addShaderEditor(string path)
+ {
+ replaceEditorInShader(path, "Thry.ShaderEditor");
+ AddThryProperty(path);
+ }
+
+ public static void AddThryProperty(Shader shader)
+ {
+ string path = AssetDatabase.GetAssetPath(shader);
+ AddThryProperty(path);
+ }
+
+ private static void AddThryProperty(string path)
+ {
+ addProperty(path, "[HideInInspector] shader_is_using_thry_editor(\"\", Float)", "0");
+ }
+
+ private void removeShaderEditor(string path)
+ {
+ revertEditor(path);
+ RemoveThryProperty(path);
+ }
+
+ private void RemoveThryProperty(string path)
+ {
+ removeProperty(path, "[HideInInspector] shader_is_using_thry_editor(\"\", Float)", "0");
+ }
+
+ private static void addProperty(string path, string property, string value)
+ {
+ string shaderCode = FileHelper.ReadFileIntoString(path);
+ string pattern = @"Properties.*\n?\s*{";
+ RegexOptions options = RegexOptions.Multiline;
+ shaderCode = Regex.Replace(shaderCode, pattern, "Properties \r\n {" + " \r\n " + property + "=" + value, options);
+
+ FileHelper.WriteStringToFile(shaderCode, path);
+ }
+
+ private void removeProperty(string path, string property, string value)
+ {
+ string shaderCode = FileHelper.ReadFileIntoString(path);
+ string pattern = @"\r?\n.*" + Regex.Escape(property) + " ?= ?" + value;
+ RegexOptions options = RegexOptions.Multiline;
+
+ shaderCode = Regex.Replace(shaderCode, pattern, "", options);
+
+ FileHelper.WriteStringToFile(shaderCode, path);
+ }
+
+ private void revertEditor(string path)
+ {
+ string shaderCode = FileHelper.ReadFileIntoString(path);
+ string pattern = @"//originalEditor.*\n";
+ Match m = Regex.Match(shaderCode, pattern);
+ if (m.Success)
+ {
+ string orignialEditor = m.Value.Replace("//originalEditor", "");
+ pattern = @"//originalEditor.*\n.*\n";
+ shaderCode = Regex.Replace(shaderCode, pattern, orignialEditor);
+ FileHelper.WriteStringToFile(shaderCode, path);
+ }
+ }
+
+ private void replaceEditorInShader(string path, string newEditor)
+ {
+ string shaderCode = FileHelper.ReadFileIntoString(path);
+ string pattern = @"CustomEditor ?.*\n";
+ Match m = Regex.Match(shaderCode, pattern);
+ if (m.Success)
+ {
+ string oldEditor = "//originalEditor" + m.Value;
+ shaderCode = Regex.Replace(shaderCode, pattern, oldEditor + "CustomEditor \"" + newEditor + "\"\r\n");
+ }
+ else
+ {
+ pattern = @"SubShader.*\r?\n?\s*{";
+ RegexOptions options = RegexOptions.Multiline;
+ shaderCode = Regex.Replace(shaderCode, pattern, "CustomEditor \"" + newEditor + "\" \r\n SubShader \r\n {", options);
+ }
+
+ FileHelper.WriteStringToFile(shaderCode, path);
+ }
+
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/EditorChanger.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/EditorChanger.cs.meta
new file mode 100644
index 00000000..693a1370
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/EditorChanger.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 2512996118f494a44972192a9fafb1a3
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/EditorStructs.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/EditorStructs.cs
new file mode 100644
index 00000000..44a71447
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/EditorStructs.cs
@@ -0,0 +1,943 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text.RegularExpressions;
+using Thry.ThryEditor;
+using UnityEditor;
+using UnityEngine;
+
+namespace Thry
+{
+ public class CRect
+ {
+ public Rect r;
+ public CRect(Rect r)
+ {
+ this.r = r;
+ }
+ }
+
+ public class InputEvent
+ {
+ public bool HadMouseDownRepaint;
+ public bool HadMouseDown;
+ int _button;
+ bool _MouseClick;
+ bool _MouseLeftClickIgnoreLocked;
+ bool _MouseRightClickIgnoreLocked;
+ bool _MouseLeftClick;
+ bool _MouseRightClick;
+
+ public bool is_alt_down;
+
+ public bool is_drag_drop_event;
+ public bool is_drop_event;
+
+ public Vector2 mouse_position;
+ public Vector2 screen_mouse_position;
+
+ public void Update(bool isLockedMaterial)
+ {
+ Event e = Event.current;
+ _button = e.button;
+ _MouseClick = e.type == EventType.MouseDown && !isLockedMaterial;
+ _MouseLeftClick = _MouseClick && _button == 0;
+ _MouseRightClick = _MouseClick && _button == 1;
+ _MouseLeftClickIgnoreLocked = e.type == EventType.MouseDown && _button == 0;
+ _MouseRightClickIgnoreLocked = e.type == EventType.MouseDown && _button == 1;
+ if (_MouseClick) HadMouseDown = _MouseClick;
+ if (HadMouseDown && e.type == EventType.Repaint)
+ {
+ HadMouseDownRepaint = true;
+ HadMouseDown = false;
+ }
+ is_alt_down = e.alt;
+ mouse_position = e.mousePosition;
+ screen_mouse_position = GUIUtility.GUIToScreenPoint(e.mousePosition);
+ is_drop_event = e.type == EventType.DragPerform;
+ is_drag_drop_event = is_drop_event || e.type == EventType.DragUpdated;
+ }
+
+ public void Use()
+ {
+ _MouseClick = false;
+ _MouseLeftClick = false;
+ _MouseRightClick = false;
+ Event.current.Use();
+ }
+
+ public bool LeftClick_IgnoreUnityUses
+ {
+ get { return _MouseLeftClick; }
+ }
+
+ public bool RightClick_IgnoreUnityUses
+ {
+ get { return _MouseRightClick; }
+ }
+
+ public bool LeftClick_IgnoreLocked
+ {
+ get { return _MouseLeftClickIgnoreLocked && Event.current.type != EventType.Used; }
+ }
+
+ public bool RightClick_IgnoreLocked
+ {
+ get { return _MouseRightClickIgnoreLocked && Event.current.type != EventType.Used; }
+ }
+
+ public bool LeftClick_IgnoreLockedAndUnityUses
+ {
+ get { return _MouseLeftClickIgnoreLocked; }
+ }
+
+ public bool RightClick_IgnoreLockedAndUnityUses
+ {
+ get { return _MouseRightClickIgnoreLocked; }
+ }
+
+ public bool Click
+ {
+ get { return _MouseClick && Event.current.type != EventType.Used; }
+ }
+
+ public bool RightClick
+ {
+ get { return _MouseRightClick && Event.current.type != EventType.Used; }
+ }
+
+ public bool LeftClick
+ {
+ get { return _MouseLeftClick && Event.current.type != EventType.Used; }
+ }
+ }
+
+ public abstract class ShaderPart
+ {
+ public ShaderEditor ActiveShaderEditor;
+
+ public int XOffset = 0;
+ public GUIContent Content;
+ public MaterialProperty MaterialProperty;
+ public System.Object PropertyData = null;
+ public PropertyOptions Options;
+ public bool DoReferencePropertiesExist = false;
+ public bool DoesReferencePropertyExist = false;
+ public bool IsHidden = false;
+ public bool IsAnimatable = false;
+ public bool IsPreset = false;
+ public bool ExemptFromLockedDisabling = false;
+ public bool IsAnimated = false;
+ public bool IsRenaming = false;
+
+ public BetterTooltips.Tooltip tooltip;
+
+ public bool has_not_searchedFor = false; //used for property search
+
+ GenericMenu contextMenu;
+
+ public ShaderPart(ShaderEditor shaderEditor, MaterialProperty prop, int xOffset, string displayName, PropertyOptions options)
+ {
+ this.ActiveShaderEditor = shaderEditor;
+ this.MaterialProperty = prop;
+ this.XOffset = xOffset;
+ this.Options = options;
+ this.Content = new GUIContent(displayName);
+ this.tooltip = new BetterTooltips.Tooltip(options.tooltip);
+ this.DoReferencePropertiesExist = options.reference_properties != null && options.reference_properties.Length > 0;
+ this.DoesReferencePropertyExist = options.reference_property != null;
+ this.IsPreset = shaderEditor.IsPresetEditor && Presets.IsPreset(shaderEditor.Materials[0], prop);
+
+ if (prop == null)
+ return;
+ if (this is ShaderHeader == false)
+ {
+ this.IsAnimatable = !DrawingData.LastPropertyDoesntAllowAnimation;
+ bool propHasDuplicate = shaderEditor.GetMaterialProperty(prop.name + "_" + shaderEditor.RenamedPropertySuffix) != null;
+ string tag = null;
+ //If prop is og, but is duplicated (locked) dont have it animateable
+ if (propHasDuplicate)
+ {
+ this.IsAnimatable = false;
+ }
+ else
+ {
+ //if prop is a duplicated or renamed get og property to check for animted status
+ if (prop.name.Contains(shaderEditor.RenamedPropertySuffix))
+ {
+ string ogName = prop.name.Substring(0, prop.name.Length - shaderEditor.RenamedPropertySuffix.Length - 1);
+ tag = ShaderOptimizer.GetAnimatedTag(MaterialProperty.targets[0] as Material, ogName);
+ }
+ else
+ {
+ tag = ShaderOptimizer.GetAnimatedTag(MaterialProperty);
+ }
+ }
+
+ this.IsAnimated = IsAnimatable && tag != "";
+ this.IsRenaming = IsAnimatable && tag == "2";
+ }
+ }
+
+ public void SetReferenceProperty(string s)
+ {
+ Options.reference_property = s;
+ this.DoesReferencePropertyExist = Options.reference_property != null;
+ }
+
+ public void SetReferenceProperties(string[] properties)
+ {
+ Options.reference_properties = properties;
+ this.DoReferencePropertiesExist = Options.reference_properties != null && Options.reference_properties.Length > 0;
+ }
+
+ public void SetTooltip(string tooltip)
+ {
+ this.tooltip.SetText(tooltip);
+ }
+
+ public abstract void DrawInternal(GUIContent content, CRect rect = null, bool useEditorIndent = false, bool isInHeader = false);
+ public abstract void CopyFromMaterial(Material m, bool isTopCall = false);
+ public abstract void CopyToMaterial(Material m, bool isTopCall = false);
+
+ public abstract void TransferFromMaterialAndGroup(Material m, ShaderPart g, bool isTopCall = false);
+
+ bool hasAddedDisabledGroup = false;
+ public void Draw(CRect rect = null, GUIContent content = null, bool useEditorIndent = false, bool isInHeader = false)
+ {
+ if (has_not_searchedFor)
+ return;
+ if (DrawingData.IsEnabled && Options.condition_enable != null)
+ {
+ hasAddedDisabledGroup = !Options.condition_enable.Test();
+ if(hasAddedDisabledGroup)
+ {
+ DrawingData.IsEnabled = !hasAddedDisabledGroup;
+ EditorGUI.BeginDisabledGroup(true);
+ }
+ }
+ if (Options.condition_show.Test())
+ {
+ PerformDraw(content, rect, useEditorIndent, isInHeader);
+ }
+ if (hasAddedDisabledGroup)
+ {
+ hasAddedDisabledGroup = false;
+ DrawingData.IsEnabled = true;
+ EditorGUI.EndDisabledGroup();
+ }
+ }
+
+ public virtual void HandleRightClickToggles(bool isInHeader)
+ {
+ if (this is ShaderGroup) return;
+ if (ShaderEditor.Input.RightClick_IgnoreLockedAndUnityUses && DrawingData.TooltipCheckRect.Contains(Event.current.mousePosition))
+ {
+ //Context menu
+ //Show context menu, if not open.
+ //If locked material only show menu for animated materials. Only show data retieving options in locked state
+ if ( (!ShaderEditor.Active.IsLockedMaterial || IsAnimated)) {
+ contextMenu = new GenericMenu();
+ if (IsAnimatable && !ShaderEditor.Active.IsLockedMaterial)
+ {
+ contextMenu.AddItem(new GUIContent("Animated (when locked)"), IsAnimated, () => { SetAnimated(!IsAnimated, false); });
+ contextMenu.AddItem(new GUIContent("Renamed (when locked)"), IsAnimated && IsRenaming, () => { SetAnimated(true, !IsRenaming); });
+ contextMenu.AddItem(new GUIContent("Locking Explanation"), false, () => { Application.OpenURL("https://www.youtube.com/watch?v=asWeDJb5LAo&ab_channel=poiyomi"); });
+ contextMenu.AddSeparator("");
+ }
+ if (ShaderEditor.Active.IsPresetEditor )
+ {
+ contextMenu.AddItem(new GUIContent("Is part of preset"), IsPreset, ToggleIsPreset);
+ contextMenu.AddSeparator("");
+ }
+ contextMenu.AddItem(new GUIContent("Copy Property Name"), false, () => { EditorGUIUtility.systemCopyBuffer = MaterialProperty.name; });
+ contextMenu.AddItem(new GUIContent("Copy Animated Property Name"), false, () => { EditorGUIUtility.systemCopyBuffer = GetAnimatedPropertyName(); });
+ contextMenu.AddItem(new GUIContent("Copy Animated Property Path"), false, CopyPropertyPath );
+ contextMenu.AddItem(new GUIContent("Copy Property as Keyframe"), false, CopyPropertyAsKeyframe);
+ contextMenu.ShowAsContext();
+ }
+ }
+ }
+
+ void ToggleIsPreset()
+ {
+ IsPreset = !IsPreset;
+ Presets.SetProperty(ActiveShaderEditor.Materials[0], MaterialProperty, IsPreset);
+ ShaderEditor.RepaintActive();
+ }
+
+ void CopyPropertyPath()
+ {
+ string path = GetAnimatedPropertyName();
+ Transform selected = Selection.activeTransform;
+ Transform root = selected;
+ while(root != null && root.GetComponent<Animator>() == null)
+ root = root.parent;
+ if (selected != null && root != null && selected != root)
+ path = AnimationUtility.CalculateTransformPath(selected, root) + "/" + path;
+ EditorGUIUtility.systemCopyBuffer = path;
+ }
+
+ string GetAnimatedPropertyName()
+ {
+ string propName = MaterialProperty.name;
+ if (IsRenaming && !ShaderEditor.Active.IsLockedMaterial) propName = propName + "_" + ShaderEditor.Active.RenamedPropertySuffix;
+ if (MaterialProperty.type == MaterialProperty.PropType.Texture) propName = propName + "_ST";
+ return propName;
+ }
+
+ void CopyPropertyAsKeyframe()
+ {
+ string path = "";
+ Transform selected = Selection.activeTransform;
+ Transform root = selected;
+ while (root != null && root.GetComponent<Animator>() == null)
+ root = root.parent;
+ if (selected != null && root != null && selected != root)
+ path = AnimationUtility.CalculateTransformPath(selected, root);
+ if(selected == null && root == null)
+ return;
+
+ Type rendererType = typeof(Renderer);
+ if (selected.GetComponent<SkinnedMeshRenderer>()) rendererType = typeof(SkinnedMeshRenderer);
+ if (selected.GetComponent<MeshRenderer>()) rendererType = typeof(MeshRenderer);
+
+ Type animationStateType = typeof(AnimationUtility).Assembly.GetType("UnityEditorInternal.AnimationWindowState");
+ Type animationKeyframeType = typeof(AnimationUtility).Assembly.GetType("UnityEditorInternal.AnimationWindowKeyframe");
+ Type animationCurveType = typeof(AnimationUtility).Assembly.GetType("UnityEditorInternal.AnimationWindowCurve");
+
+ FieldInfo clipboardField = animationStateType.GetField("s_KeyframeClipboard", BindingFlags.NonPublic | BindingFlags.Static);
+
+ Type keyframeListType = typeof(List<>).MakeGenericType(animationKeyframeType);
+ IList keyframeList = (IList)Activator.CreateInstance(keyframeListType);
+
+ AnimationClip clip = new AnimationClip();
+
+ string propertyname = "material." + GetAnimatedPropertyName();
+ if (MaterialProperty.type == MaterialProperty.PropType.Float || MaterialProperty.type == MaterialProperty.PropType.Range)
+ {
+ clip.SetCurve(path, rendererType, propertyname, new AnimationCurve(new Keyframe(0, MaterialProperty.floatValue)));
+ keyframeList.Add(ClipToKeyFrame(animationCurveType, clip, path, "", rendererType));
+ }
+ else if(MaterialProperty.type == MaterialProperty.PropType.Color)
+ {
+ clip.SetCurve(path, rendererType, propertyname + ".r", new AnimationCurve(new Keyframe(0, MaterialProperty.colorValue.r)));
+ clip.SetCurve(path, rendererType, propertyname + ".g", new AnimationCurve(new Keyframe(0, MaterialProperty.colorValue.g)));
+ clip.SetCurve(path, rendererType, propertyname + ".b", new AnimationCurve(new Keyframe(0, MaterialProperty.colorValue.b)));
+ clip.SetCurve(path, rendererType, propertyname + ".a", new AnimationCurve(new Keyframe(0, MaterialProperty.colorValue.a)));
+ keyframeList.Add(ClipToKeyFrame(animationCurveType, clip, path, ".r", rendererType));
+ keyframeList.Add(ClipToKeyFrame(animationCurveType, clip, path, ".g", rendererType));
+ keyframeList.Add(ClipToKeyFrame(animationCurveType, clip, path, ".b", rendererType));
+ keyframeList.Add(ClipToKeyFrame(animationCurveType, clip, path, ".a", rendererType));
+ }else if(MaterialProperty.type == MaterialProperty.PropType.Vector)
+ {
+ clip.SetCurve(path, rendererType, propertyname + ".x", new AnimationCurve(new Keyframe(0, MaterialProperty.vectorValue.x)));
+ clip.SetCurve(path, rendererType, propertyname + ".y", new AnimationCurve(new Keyframe(0, MaterialProperty.vectorValue.y)));
+ clip.SetCurve(path, rendererType, propertyname + ".z", new AnimationCurve(new Keyframe(0, MaterialProperty.vectorValue.z)));
+ clip.SetCurve(path, rendererType, propertyname + ".w", new AnimationCurve(new Keyframe(0, MaterialProperty.vectorValue.w)));
+ keyframeList.Add(ClipToKeyFrame(animationCurveType, clip, path, ".x", rendererType));
+ keyframeList.Add(ClipToKeyFrame(animationCurveType, clip, path, ".y", rendererType));
+ keyframeList.Add(ClipToKeyFrame(animationCurveType, clip, path, ".z", rendererType));
+ keyframeList.Add(ClipToKeyFrame(animationCurveType, clip, path, ".w", rendererType));
+ }else if(MaterialProperty.type == MaterialProperty.PropType.Texture)
+ {
+ clip.SetCurve(path, rendererType, propertyname + ".x", new AnimationCurve(new Keyframe(0, MaterialProperty.textureScaleAndOffset.x)));
+ clip.SetCurve(path, rendererType, propertyname + ".y", new AnimationCurve(new Keyframe(0, MaterialProperty.textureScaleAndOffset.y)));
+ clip.SetCurve(path, rendererType, propertyname + ".z", new AnimationCurve(new Keyframe(0, MaterialProperty.textureScaleAndOffset.z)));
+ clip.SetCurve(path, rendererType, propertyname + ".w", new AnimationCurve(new Keyframe(0, MaterialProperty.textureScaleAndOffset.w)));
+ keyframeList.Add(ClipToKeyFrame(animationCurveType, clip, path, ".x", rendererType));
+ keyframeList.Add(ClipToKeyFrame(animationCurveType, clip, path, ".y", rendererType));
+ keyframeList.Add(ClipToKeyFrame(animationCurveType, clip, path, ".z", rendererType));
+ keyframeList.Add(ClipToKeyFrame(animationCurveType, clip, path, ".w", rendererType));
+ }
+ clipboardField.SetValue(null, keyframeList);
+ }
+
+ object ClipToKeyFrame(Type animationCurveType, AnimationClip clip, string path, string propertyPostFix, Type rendererType)
+ {
+ FieldInfo curvesField = animationCurveType.GetField("m_Keyframes", BindingFlags.Instance | BindingFlags.Public);
+
+ object windowCurve = Activator.CreateInstance(animationCurveType, clip,
+ EditorCurveBinding.FloatCurve(path, rendererType, "material." + GetAnimatedPropertyName() + propertyPostFix), typeof(float));
+ IEnumerator enumerator = (curvesField.GetValue(windowCurve) as IList).GetEnumerator();
+ enumerator.MoveNext();
+ return enumerator.Current;
+ }
+
+ public void SetAnimated(bool animated, bool renamed)
+ {
+ if (IsAnimated == animated && IsRenaming == renamed) return;
+ IsAnimated = animated;
+ IsRenaming = renamed;
+ ShaderOptimizer.SetAnimatedTag(MaterialProperty, IsAnimated ? (IsRenaming ? "2" : "1") : "");
+ }
+
+ private void PerformDraw(GUIContent content, CRect rect, bool useEditorIndent, bool isInHeader = false)
+ {
+ if (content == null)
+ content = this.Content;
+ EditorGUI.BeginChangeCheck();
+ DrawInternal(content, rect, useEditorIndent, isInHeader);
+
+ if(this is TextureProperty == false) DrawingData.TooltipCheckRect = DrawingData.LastGuiObjectRect;
+ DrawingData.TooltipCheckRect.width = EditorGUIUtility.labelWidth;
+
+ HandleRightClickToggles(isInHeader);
+
+ if (EditorGUI.EndChangeCheck())
+ {
+ if(Options.on_value_actions != null)
+ foreach (PropertyValueAction action in Options.on_value_actions)
+ {
+ action.Execute(MaterialProperty);
+ }
+ //Check if property is being animated
+ if(this is ShaderProperty && ActiveShaderEditor.ActiveRenderer != null && ActiveShaderEditor.IsInAnimationMode && IsAnimatable && !IsAnimated)
+ {
+ if (MaterialProperty.type == MaterialProperty.PropType.Texture ?
+ AnimationMode.IsPropertyAnimated(ActiveShaderEditor.ActiveRenderer, "material." + MaterialProperty.name + "_ST.x" ) :
+ AnimationMode.IsPropertyAnimated(ActiveShaderEditor.ActiveRenderer, "material." + MaterialProperty.name))
+ SetAnimated(true, false);
+ }
+ }
+
+ if (IsAnimatable && IsAnimated) DrawLockedAnimated();
+ if (IsPreset) DrawPresetProperty();
+
+ tooltip.ConditionalDraw(DrawingData.TooltipCheckRect);
+
+ //Click testing
+ if(Event.current.type == EventType.MouseDown && DrawingData.LastGuiObjectRect.Contains(ShaderEditor.Input.mouse_position))
+ {
+ if ((ShaderEditor.Input.is_alt_down && Options.altClick != null)) Options.altClick.Perform();
+ else if (Options.onClick != null) Options.onClick.Perform();
+ }
+ }
+
+ private void DrawLockedAnimated()
+ {
+ Rect r = new Rect(14, DrawingData.TooltipCheckRect.y + 2, 16, 16);
+ //GUI.DrawTexture(r, is_renaming ? Styles.texture_animated_renamed : Styles.texture_animated, ScaleMode.StretchToFill, true);
+ if (IsRenaming) GUI.Label(r, "RA", Styles.animatedIndicatorStyle);
+ else GUI.Label(r, "A", Styles.animatedIndicatorStyle);
+ }
+
+ private void DrawPresetProperty()
+ {
+ Rect r = new Rect(2, DrawingData.TooltipCheckRect.y + 2, 8, 16);
+ //GUI.DrawTexture(r, Styles.texture_preset, ScaleMode.StretchToFill, true);
+ GUI.Label(r, "P", Styles.cyanStyle);
+ }
+ }
+
+ public class ShaderGroup : ShaderPart
+ {
+ public List<ShaderPart> parts = new List<ShaderPart>();
+
+ public ShaderGroup(ShaderEditor shaderEditor) : base(shaderEditor, null, 0, "", new PropertyOptions())
+ {
+
+ }
+
+ public ShaderGroup(ShaderEditor shaderEditor, PropertyOptions options) : base(shaderEditor, null, 0, "", new PropertyOptions())
+ {
+ this.Options = options;
+ }
+
+ public ShaderGroup(ShaderEditor shaderEditor, MaterialProperty prop, MaterialEditor materialEditor, string displayName, int xOffset, PropertyOptions options) : base(shaderEditor, prop, xOffset, displayName, options)
+ {
+
+ }
+
+ public void addPart(ShaderPart part)
+ {
+ parts.Add(part);
+ }
+
+ public override void CopyFromMaterial(Material m, bool isTopCall = false)
+ {
+ if (Options.reference_property != null)
+ ActiveShaderEditor.PropertyDictionary[Options.reference_property].CopyFromMaterial(m);
+ foreach (ShaderPart p in parts)
+ p.CopyFromMaterial(m);
+ if (isTopCall) ActiveShaderEditor.ApplyDrawers();
+ }
+
+ public override void CopyToMaterial(Material m, bool isTopCall = false)
+ {
+ if (Options.reference_property != null)
+ ActiveShaderEditor.PropertyDictionary[Options.reference_property].CopyToMaterial(m);
+ foreach (ShaderPart p in parts)
+ p.CopyToMaterial(m);
+ if (isTopCall) MaterialEditor.ApplyMaterialPropertyDrawers(m);
+ }
+
+ public override void DrawInternal(GUIContent content, CRect rect = null, bool useEditorIndent = false, bool isInHeader = false)
+ {
+ foreach (ShaderPart part in parts)
+ {
+ part.Draw();
+ }
+ }
+
+ public override void TransferFromMaterialAndGroup(Material m, ShaderPart p, bool isTopCall = false)
+ {
+ if (p is ShaderGroup == false) return;
+ ShaderGroup group = p as ShaderGroup;
+ if (Options.reference_property != null && group.Options.reference_property != null)
+ ActiveShaderEditor.PropertyDictionary[Options.reference_property].TransferFromMaterialAndGroup(m, group.ActiveShaderEditor.PropertyDictionary[group.Options.reference_property]);
+ for(int i=0;i<group.parts.Count && i < parts.Count; i++)
+ {
+ parts[i].TransferFromMaterialAndGroup(m, group.parts[i]);
+ }
+ if (isTopCall) ActiveShaderEditor.ApplyDrawers();
+ }
+ }
+
+ public class ShaderHeader : ShaderGroup
+ {
+ private ThryHeaderDrawer headerDrawer;
+ private bool isLegacy;
+
+ public ShaderHeader(ShaderEditor shaderEditor) : base(shaderEditor)
+ {
+ this.headerDrawer = new ThryHeaderDrawer();
+ }
+
+ public ShaderHeader(ShaderEditor shaderEditor, MaterialProperty prop, MaterialEditor materialEditor, string displayName, int xOffset, PropertyOptions options) : base(shaderEditor, prop, materialEditor, displayName, xOffset, options)
+ {
+ if(DrawingData.LastPropertyDrawerType == DrawerType.Header)
+ {
+ //new header setup with drawer
+ this.headerDrawer = DrawingData.LastPropertyDrawer as ThryHeaderDrawer;
+ }
+ else
+ {
+ //legacy setup with HideInInspector
+ this.headerDrawer = new ThryHeaderDrawer();
+ isLegacy = true;
+ }
+ this.headerDrawer.xOffset = xOffset;
+ }
+
+ public string GetEndProperty()
+ {
+ return headerDrawer.GetEndProperty();
+ }
+
+ public override void DrawInternal(GUIContent content, CRect rect = null, bool useEditorIndent = false, bool isInHeader = false)
+ {
+ ActiveShaderEditor.CurrentProperty = this;
+ EditorGUI.BeginChangeCheck();
+ Rect position = GUILayoutUtility.GetRect(content, Styles.dropDownHeader);
+ if (isLegacy) headerDrawer.OnGUI(position, this.MaterialProperty, content, ActiveShaderEditor.Editor);
+ else ActiveShaderEditor.Editor.ShaderProperty(position, this.MaterialProperty, content);
+ Rect headerRect = DrawingData.LastGuiObjectHeaderRect;
+ if (this.headerDrawer.is_expanded)
+ {
+ EditorGUILayout.Space();
+ foreach (ShaderPart part in parts)
+ {
+ part.Draw();
+ }
+ EditorGUILayout.Space();
+ }
+ if (EditorGUI.EndChangeCheck())
+ HandleLinkedMaterials();
+ DrawingData.LastGuiObjectHeaderRect = headerRect;
+ DrawingData.LastGuiObjectRect = headerRect;
+ }
+
+ private void HandleLinkedMaterials()
+ {
+ List<Material> linked_materials = MaterialLinker.GetLinked(MaterialProperty);
+ if (linked_materials != null)
+ foreach (Material m in linked_materials)
+ this.CopyToMaterial(m);
+ }
+ }
+
+ public class ShaderProperty : ShaderPart
+ {
+ public float setFloat;
+ public bool updateFloat;
+
+ public bool doCustomDrawLogic = false;
+ public bool doForceIntoOneLine = false;
+ public bool doDrawTwoFields = false;
+
+ //Done for e.g. Vectors cause they draw in 2 lines for some fucking reasons
+ public bool doCustomHeightOffset = false;
+ public float customHeightOffset = 0;
+
+ private int property_index = 0;
+
+ public string keyword;
+
+ public ShaderProperty(ShaderEditor shaderEditor, MaterialProperty materialProperty, string displayName, int xOffset, PropertyOptions options, bool forceOneLine, int property_index) : base(shaderEditor, materialProperty, xOffset, displayName, options)
+ {
+ this.doCustomDrawLogic = false;
+ this.doForceIntoOneLine = forceOneLine;
+
+ if (materialProperty.type == MaterialProperty.PropType.Vector && forceOneLine == false)
+ {
+ this.doCustomHeightOffset = !DrawingData.LastPropertyUsedCustomDrawer;
+ this.customHeightOffset = -EditorGUIUtility.singleLineHeight;
+ }
+
+ this.doDrawTwoFields = options.reference_property != null;
+
+ this.property_index = property_index;
+ }
+
+ public override void CopyFromMaterial(Material m, bool isTopCall = false)
+ {
+ MaterialHelper.CopyPropertyValueFromMaterial(MaterialProperty, m);
+ if (keyword != null) SetKeyword(ActiveShaderEditor.Materials, m.GetFloat(MaterialProperty.name)==1);
+ if (IsAnimatable)
+ {
+ ShaderOptimizer.CopyAnimatedTagFromMaterial(m, MaterialProperty);
+ }
+ this.IsAnimated = IsAnimatable && ShaderOptimizer.GetAnimatedTag(MaterialProperty) != "";
+ this.IsRenaming = IsAnimatable && ShaderOptimizer.GetAnimatedTag(MaterialProperty) == "2";
+
+ if (isTopCall) ActiveShaderEditor.ApplyDrawers();
+ }
+
+ public override void CopyToMaterial(Material m, bool isTopCall = false)
+ {
+ MaterialHelper.CopyPropertyValueToMaterial(MaterialProperty, m);
+ if (keyword != null) SetKeyword(m, MaterialProperty.floatValue == 1);
+ if (IsAnimatable)
+ ShaderOptimizer.CopyAnimatedTagToMaterials(new Material[] { m }, MaterialProperty);
+
+ if (isTopCall) MaterialEditor.ApplyMaterialPropertyDrawers(m);
+ }
+
+ private void SetKeyword(Material[] materials, bool enabled)
+ {
+ if (enabled) foreach (Material m in materials) m.EnableKeyword(keyword);
+ else foreach (Material m in materials) m.DisableKeyword(keyword);
+ }
+
+ private void SetKeyword(Material m, bool enabled)
+ {
+ if (enabled) m.EnableKeyword(keyword);
+ else m.DisableKeyword(keyword);
+ }
+
+ public override void DrawInternal(GUIContent content, CRect rect = null, bool useEditorIndent = false, bool isInHeader = false)
+ {
+ PreDraw();
+ ActiveShaderEditor.CurrentProperty = this;
+ this.MaterialProperty = ActiveShaderEditor.Properties[property_index];
+ if (ActiveShaderEditor.IsLockedMaterial)
+ EditorGUI.BeginDisabledGroup(!(IsAnimatable && (IsAnimated || IsRenaming)) && !ExemptFromLockedDisabling);
+ int oldIndentLevel = EditorGUI.indentLevel;
+ if (!useEditorIndent)
+ EditorGUI.indentLevel = XOffset + 1;
+
+ if (doCustomDrawLogic)
+ {
+ DrawDefault();
+ }
+ else if (doDrawTwoFields)
+ {
+ Rect r = GUILayoutUtility.GetRect(content, Styles.vectorPropertyStyle);
+ float labelWidth = (r.width - EditorGUIUtility.labelWidth) / 2; ;
+ r.width -= labelWidth;
+ ActiveShaderEditor.Editor.ShaderProperty(r, this.MaterialProperty, content);
+
+ r.x += r.width;
+ r.width = labelWidth;
+ float prevLabelW = EditorGUIUtility.labelWidth;
+ EditorGUIUtility.labelWidth = 0;
+ ActiveShaderEditor.PropertyDictionary[Options.reference_property].Draw(new CRect(r), new GUIContent());
+ EditorGUIUtility.labelWidth = prevLabelW;
+ }
+ else if (doForceIntoOneLine)
+ {
+ ActiveShaderEditor.Editor.ShaderProperty(GUILayoutUtility.GetRect(content, Styles.vectorPropertyStyle), this.MaterialProperty, content);
+ }else if (doCustomHeightOffset)
+ {
+ ActiveShaderEditor.Editor.ShaderProperty(
+ GUILayoutUtility.GetRect(EditorGUIUtility.currentViewWidth, ActiveShaderEditor.Editor.GetPropertyHeight(this.MaterialProperty, content.text) + customHeightOffset)
+ , this.MaterialProperty, content);
+ }
+ else if (rect != null)
+ {
+ ActiveShaderEditor.Editor.ShaderProperty(rect.r, this.MaterialProperty, content);
+ }
+ else
+ {
+ ActiveShaderEditor.Editor.ShaderProperty(this.MaterialProperty, content);
+ }
+
+ EditorGUI.indentLevel = oldIndentLevel;
+ if (rect == null) DrawingData.LastGuiObjectRect = GUILayoutUtility.GetLastRect();
+ else DrawingData.LastGuiObjectRect = rect.r;
+ if (ActiveShaderEditor.IsLockedMaterial)
+ EditorGUI.EndDisabledGroup();
+ }
+
+ public virtual void PreDraw() { }
+
+ public virtual void DrawDefault() { }
+
+ public override void TransferFromMaterialAndGroup(Material m, ShaderPart p, bool isTopCall = false)
+ {
+ if (MaterialProperty.type != p.MaterialProperty.type) return;
+ MaterialHelper.CopyMaterialValueFromProperty(MaterialProperty, p.MaterialProperty);
+ if (keyword != null) SetKeyword(ActiveShaderEditor.Materials, m.GetFloat(p.MaterialProperty.name) == 1);
+ if (IsAnimatable && p.IsAnimatable)
+ ShaderOptimizer.CopyAnimatedTagFromProperty(p.MaterialProperty, MaterialProperty);
+ this.IsAnimated = IsAnimatable && ShaderOptimizer.GetAnimatedTag(MaterialProperty) != "";
+ this.IsRenaming = IsAnimatable && ShaderOptimizer.GetAnimatedTag(MaterialProperty) == "2";
+
+ if (isTopCall) ActiveShaderEditor.ApplyDrawers();
+ }
+ }
+
+ public class TextureProperty : ShaderProperty
+ {
+ public bool showFoldoutProperties = false;
+ public bool hasFoldoutProperties = false;
+ public bool hasScaleOffset = false;
+
+ public TextureProperty(ShaderEditor shaderEditor, MaterialProperty materialProperty, string displayName, int xOffset, PropertyOptions options, bool hasScaleOffset, bool forceThryUI, int property_index) : base(shaderEditor, materialProperty, displayName, xOffset, options, false, property_index)
+ {
+ doCustomDrawLogic = forceThryUI;
+ this.hasScaleOffset = hasScaleOffset;
+ this.hasFoldoutProperties = hasScaleOffset || DoReferencePropertiesExist;
+ }
+
+ public override void PreDraw()
+ {
+ DrawingData.CurrentTextureProperty = this;
+ }
+
+ public override void DrawDefault()
+ {
+ Rect pos = GUILayoutUtility.GetRect(Content, Styles.vectorPropertyStyle);
+ GuiHelper.ConfigTextureProperty(pos, MaterialProperty, Content, ActiveShaderEditor.Editor, hasFoldoutProperties);
+ DrawingData.LastGuiObjectRect = pos;
+ }
+
+ public override void CopyFromMaterial(Material m, bool isTopCall = false)
+ {
+ MaterialHelper.CopyPropertyValueFromMaterial(MaterialProperty, m);
+ CopyReferencePropertiesFromMaterial(m);
+
+ if (isTopCall) ActiveShaderEditor.ApplyDrawers();
+ }
+
+ public override void CopyToMaterial(Material m, bool isTopCall = false)
+ {
+ MaterialHelper.CopyPropertyValueToMaterial(MaterialProperty, m);
+ CopyReferencePropertiesToMaterial(m);
+
+ if (isTopCall) MaterialEditor.ApplyMaterialPropertyDrawers(m);
+ }
+
+ public override void TransferFromMaterialAndGroup(Material m, ShaderPart p, bool isTopCall = false)
+ {
+ if (MaterialProperty.type != p.MaterialProperty.type) return;
+ MaterialHelper.CopyMaterialValueFromProperty(MaterialProperty, p.MaterialProperty);
+ TransferReferencePropertiesToMaterial(m, p);
+ }
+ private void TransferReferencePropertiesToMaterial(Material target, ShaderPart p)
+ {
+ if (p.Options.reference_properties == null || this.Options.reference_properties == null) return;
+ for (int i = 0; i < p.Options.reference_properties.Length && i < Options.reference_properties.Length; i++)
+ {
+ if (ActiveShaderEditor.PropertyDictionary.ContainsKey(this.Options.reference_properties[i]) == false) continue;
+
+ ShaderProperty targetP = ActiveShaderEditor.PropertyDictionary[this.Options.reference_properties[i]];
+ ShaderProperty sourceP = p.ActiveShaderEditor.PropertyDictionary[p.Options.reference_properties[i]];
+ MaterialHelper.CopyMaterialValueFromProperty(targetP.MaterialProperty, sourceP.MaterialProperty);
+ }
+ }
+
+ private void CopyReferencePropertiesToMaterial(Material target)
+ {
+ if (Options.reference_properties != null)
+ foreach (string r_property in Options.reference_properties)
+ {
+ ShaderProperty property = ActiveShaderEditor.PropertyDictionary[r_property];
+ MaterialHelper.CopyPropertyValueToMaterial(property.MaterialProperty, target);
+ }
+ }
+
+ private void CopyReferencePropertiesFromMaterial(Material source)
+ {
+ if (Options.reference_properties != null)
+ foreach (string r_property in Options.reference_properties)
+ {
+ ShaderProperty property = ActiveShaderEditor.PropertyDictionary[r_property];
+ MaterialHelper.CopyPropertyValueFromMaterial(property.MaterialProperty, source);
+ }
+ }
+ }
+
+ public class ShaderHeaderProperty : ShaderPart
+ {
+ public ShaderHeaderProperty(ShaderEditor shaderEditor, MaterialProperty materialProperty, string displayName, int xOffset, PropertyOptions options, bool forceOneLine) : base(shaderEditor, materialProperty, xOffset, displayName, options)
+ {
+ }
+
+ public override void HandleRightClickToggles(bool isInHeader)
+ {
+
+ }
+
+ public override void DrawInternal(GUIContent content, CRect rect = null, bool useEditorIndent = false, bool isInHeader = false)
+ {
+ if (rect == null)
+ {
+ if (Options.texture != null && Options.texture.name != null)
+ {
+ //is texutre draw
+ content = new GUIContent(Options.texture.loaded_texture, content.tooltip);
+ int height = Options.texture.height;
+ int width = (int)((float)Options.texture.loaded_texture.width / Options.texture.loaded_texture.height * height);
+ Rect control = EditorGUILayout.GetControlRect(false, height-18);
+ Rect r = new Rect((control.width-width)/2,control.y,width, height);
+ GUI.DrawTexture(r, Options.texture.loaded_texture);
+ }
+ }
+ else
+ {
+ //is text draw
+ Rect headerrect = new Rect(0, rect.r.y, rect.r.width, 18);
+ EditorGUI.LabelField(headerrect, "<size=16>" + this.Content.text + "</size>", Styles.masterLabel);
+ DrawingData.LastGuiObjectRect = headerrect;
+ }
+ }
+
+ public override void CopyFromMaterial(Material m, bool isTopCall = false)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public override void CopyToMaterial(Material m, bool isTopCall = false)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public override void TransferFromMaterialAndGroup(Material m, ShaderPart p, bool isTopCall = false)
+ {
+ throw new System.NotImplementedException();
+ }
+ }
+
+ public class InstancingProperty : ShaderProperty
+ {
+ public InstancingProperty(ShaderEditor shaderEditor, MaterialProperty materialProperty, string displayName, int xOffset, PropertyOptions options, bool forceOneLine) : base(shaderEditor, materialProperty, displayName, xOffset, options, forceOneLine, 0)
+ {
+ doCustomDrawLogic = true;
+ }
+
+ public override void DrawDefault()
+ {
+ ActiveShaderEditor.Editor.EnableInstancingField();
+ }
+ }
+ public class GIProperty : ShaderProperty
+ {
+ public GIProperty(ShaderEditor shaderEditor, MaterialProperty materialProperty, string displayName, int xOffset, PropertyOptions options, bool forceOneLine) : base(shaderEditor, materialProperty, displayName, xOffset, options, forceOneLine, 0)
+ {
+ doCustomDrawLogic = true;
+ }
+
+ public override void DrawDefault()
+ {
+ LightmapEmissionFlagsProperty(XOffset, false);
+ }
+
+ public static readonly GUIContent lightmapEmissiveLabel = EditorGUIUtility.TrTextContent("Global Illumination", "Controls if the emission is baked or realtime.\n\nBaked only has effect in scenes where baked global illumination is enabled.\n\nRealtime uses realtime global illumination if enabled in the scene. Otherwise the emission won't light up other objects.");
+ public static GUIContent[] lightmapEmissiveStrings = { EditorGUIUtility.TrTextContent("Realtime"), EditorGUIUtility.TrTextContent("Baked"), EditorGUIUtility.TrTextContent("None") };
+ public static int[] lightmapEmissiveValues = { (int)MaterialGlobalIlluminationFlags.RealtimeEmissive, (int)MaterialGlobalIlluminationFlags.BakedEmissive, (int)MaterialGlobalIlluminationFlags.None };
+
+ public static void FixupEmissiveFlag(Material mat)
+ {
+ if (mat == null)
+ throw new System.ArgumentNullException("mat");
+
+ mat.globalIlluminationFlags = FixupEmissiveFlag(mat.GetColor("_EmissionColor"), mat.globalIlluminationFlags);
+ }
+
+ public static MaterialGlobalIlluminationFlags FixupEmissiveFlag(Color col, MaterialGlobalIlluminationFlags flags)
+ {
+ if ((flags & MaterialGlobalIlluminationFlags.BakedEmissive) != 0 && col.maxColorComponent == 0.0f) // flag black baked
+ flags |= MaterialGlobalIlluminationFlags.EmissiveIsBlack;
+ else if (flags != MaterialGlobalIlluminationFlags.EmissiveIsBlack) // clear baked flag on everything else, unless it's explicity disabled
+ flags &= MaterialGlobalIlluminationFlags.AnyEmissive;
+ return flags;
+ }
+
+ public void LightmapEmissionFlagsProperty(int indent, bool enabled)
+ {
+ LightmapEmissionFlagsProperty(indent, enabled, false);
+ }
+
+ public void LightmapEmissionFlagsProperty(int indent, bool enabled, bool ignoreEmissionColor)
+ {
+ // Calculate isMixed
+ MaterialGlobalIlluminationFlags any_em = MaterialGlobalIlluminationFlags.AnyEmissive;
+ MaterialGlobalIlluminationFlags giFlags = ActiveShaderEditor.Materials[0].globalIlluminationFlags & any_em;
+ bool isMixed = false;
+ for (int i = 1; i < ActiveShaderEditor.Materials.Length; i++)
+ {
+ if((ActiveShaderEditor.Materials[i].globalIlluminationFlags & any_em) != giFlags)
+ {
+ isMixed = true;
+ break;
+ }
+ }
+
+ EditorGUI.BeginChangeCheck();
+
+ // Show popup
+ EditorGUI.showMixedValue = isMixed;
+ giFlags = (MaterialGlobalIlluminationFlags)EditorGUILayout.IntPopup(lightmapEmissiveLabel, (int)giFlags, lightmapEmissiveStrings, lightmapEmissiveValues);
+ EditorGUI.showMixedValue = false;
+
+ // Apply flags. But only the part that this tool modifies (RealtimeEmissive, BakedEmissive, None)
+ bool applyFlags = EditorGUI.EndChangeCheck();
+ foreach (Material mat in ActiveShaderEditor.Materials)
+ {
+ mat.globalIlluminationFlags = applyFlags ? giFlags : mat.globalIlluminationFlags;
+ if (!ignoreEmissionColor)
+ {
+ FixupEmissiveFlag(mat);
+ }
+ }
+ }
+ }
+ public class DSGIProperty : ShaderProperty
+ {
+ public DSGIProperty(ShaderEditor shaderEditor, MaterialProperty materialProperty, string displayName, int xOffset, PropertyOptions options, bool forceOneLine) : base(shaderEditor, materialProperty, displayName, xOffset, options, forceOneLine, 0)
+ {
+ doCustomDrawLogic = true;
+ }
+
+ public override void DrawDefault()
+ {
+ ActiveShaderEditor.Editor.DoubleSidedGIField();
+ }
+ }
+ public class LocaleProperty : ShaderProperty
+ {
+ public LocaleProperty(ShaderEditor shaderEditor, MaterialProperty materialProperty, string displayName, int xOffset, PropertyOptions options, bool forceOneLine) : base(shaderEditor, materialProperty, displayName, xOffset, options, forceOneLine, 0)
+ {
+ doCustomDrawLogic = true;
+ }
+
+ public override void DrawDefault()
+ {
+ GuiHelper.DrawLocaleSelection(this.Content, ActiveShaderEditor.Locale.available_locales, ActiveShaderEditor.Locale.selected_locale_index);
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/EditorStructs.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/EditorStructs.cs.meta
new file mode 100644
index 00000000..9a8cc3c7
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/EditorStructs.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 02b9c440c777db4458c2284c28736ca0
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/GUIHelper.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/GUIHelper.cs
new file mode 100644
index 00000000..c49e8010
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/GUIHelper.cs
@@ -0,0 +1,1049 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Threading.Tasks;
+using UnityEditor;
+using UnityEngine;
+
+namespace Thry
+{
+ public class GuiHelper
+ {
+
+ public static void ConfigTextureProperty(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor, bool hasFoldoutProperties, bool skip_drag_and_drop_handling = false)
+ {
+ switch (Config.Singleton.default_texture_type)
+ {
+ case TextureDisplayType.small:
+ SmallTextureProperty(position, prop, label, editor, hasFoldoutProperties);
+ break;
+ case TextureDisplayType.big:
+ if (DrawingData.CurrentTextureProperty.DoReferencePropertiesExist || DrawingData.CurrentTextureProperty.DoesReferencePropertyExist)
+ StylizedBigTextureProperty(position, prop, label, editor, hasFoldoutProperties);
+ else
+ BigTextureProperty(position, prop, label, editor, DrawingData.CurrentTextureProperty.hasScaleOffset);
+ break;
+
+ case TextureDisplayType.stylized_big:
+ StylizedBigTextureProperty(position, prop, label, editor, hasFoldoutProperties, skip_drag_and_drop_handling);
+ break;
+ }
+ }
+
+ public static void SmallTextureProperty(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor, bool hasFoldoutProperties, Action extraFoldoutGUI = null)
+ {
+ Rect thumbnailPos = position;
+ Rect foloutClickCheck = position;
+ Rect tooltipRect = position;
+ thumbnailPos.x += hasFoldoutProperties ? 20 : 0;
+ editor.TexturePropertyMiniThumbnail(thumbnailPos, prop, label.text, label.tooltip);
+ if (DrawingData.CurrentTextureProperty.DoesReferencePropertyExist)
+ {
+ ShaderProperty property = ShaderEditor.Active.PropertyDictionary[DrawingData.CurrentTextureProperty.Options.reference_property];
+ Rect r = position;
+ r.x += EditorGUIUtility.labelWidth - CurrentIndentWidth();
+ r.width -= EditorGUIUtility.labelWidth - CurrentIndentWidth();
+ foloutClickCheck.width -= r.width;
+ property.Draw(new CRect(r), new GUIContent());
+ property.tooltip.ConditionalDraw(r);
+ }
+ if (hasFoldoutProperties && DrawingData.CurrentTextureProperty != null)
+ {
+ //draw dropdown triangle
+ thumbnailPos.x += DrawingData.CurrentTextureProperty.XOffset * 15;
+ //This is an invisible button with zero functionality. But it needs to be here so that the triangle click reacts fast
+ if (GUI.Button(thumbnailPos, "", GUIStyle.none)) { }
+ if (Event.current.type == EventType.Repaint)
+ EditorStyles.foldout.Draw(thumbnailPos, false, false, DrawingData.CurrentTextureProperty.showFoldoutProperties, false);
+
+ if (DrawingData.IsEnabled)
+ {
+ //sub properties
+ if (DrawingData.CurrentTextureProperty.showFoldoutProperties)
+ {
+ EditorGUI.indentLevel += 2;
+ extraFoldoutGUI?.Invoke();
+ if (DrawingData.CurrentTextureProperty.hasScaleOffset)
+ {
+ ShaderEditor.Active.Editor.TextureScaleOffsetProperty(prop);
+ tooltipRect.height += GUILayoutUtility.GetLastRect().height;
+ }
+ //In case of locked material end disabled group here to allow editing of sub properties
+ if (ShaderEditor.Active.IsLockedMaterial) EditorGUI.EndDisabledGroup();
+
+ PropertyOptions options = DrawingData.CurrentTextureProperty.Options;
+ if (options.reference_properties != null)
+ foreach (string r_property in options.reference_properties)
+ {
+ ShaderProperty property = ShaderEditor.Active.PropertyDictionary[r_property];
+ property.Draw(useEditorIndent: true);
+ }
+
+ //readd disabled group
+ if (ShaderEditor.Active.IsLockedMaterial) EditorGUI.BeginDisabledGroup(false);
+
+ EditorGUI.indentLevel -= 2;
+ }
+ if (ShaderEditor.Input.LeftClick_IgnoreLockedAndUnityUses && foloutClickCheck.Contains(Event.current.mousePosition))
+ {
+ ShaderEditor.Input.Use();
+ DrawingData.CurrentTextureProperty.showFoldoutProperties = !DrawingData.CurrentTextureProperty.showFoldoutProperties;
+ }
+ }
+ }
+
+ Rect object_rect = new Rect(position);
+ object_rect.height = GUILayoutUtility.GetLastRect().y - object_rect.y + GUILayoutUtility.GetLastRect().height;
+ DrawingData.LastGuiObjectRect = object_rect;
+ DrawingData.TooltipCheckRect = tooltipRect;
+ }
+
+ public static void BigTextureProperty(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor, bool scaleOffset)
+ {
+ Rect rect = GUILayoutUtility.GetRect(label, Styles.bigTextureStyle);
+ float defaultLabelWidth = EditorGUIUtility.labelWidth;
+ float defaultFieldWidth = EditorGUIUtility.fieldWidth;
+ editor.SetDefaultGUIWidths();
+ editor.TextureProperty(position, prop, label.text, label.tooltip, scaleOffset);
+ EditorGUIUtility.labelWidth = defaultLabelWidth;
+ EditorGUIUtility.fieldWidth = defaultFieldWidth;
+ Rect object_rect = new Rect(position);
+ object_rect.height += rect.height;
+ DrawingData.LastGuiObjectRect = object_rect;
+ DrawingData.TooltipCheckRect = object_rect;
+ }
+
+ static int s_texturePickerWindow = -1;
+ static MaterialProperty s_texturePickerWindowProperty = null;
+ public static void StylizedBigTextureProperty(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor, bool hasFoldoutProperties, bool skip_drag_and_drop_handling = false)
+ {
+ position.x += (EditorGUI.indentLevel) * 15;
+ position.width -= (EditorGUI.indentLevel) * 15;
+ Rect rect = GUILayoutUtility.GetRect(label, Styles.bigTextureStyle);
+ rect.x += (EditorGUI.indentLevel) * 15;
+ rect.width -= (EditorGUI.indentLevel) * 15;
+ Rect border = new Rect(rect);
+ border.position = new Vector2(border.x, border.y - position.height);
+ border.height += position.height;
+
+ if (DrawingData.CurrentTextureProperty.DoReferencePropertiesExist)
+ {
+ border.height += 8;
+ foreach (string r_property in DrawingData.CurrentTextureProperty.Options.reference_properties)
+ {
+ border.height += editor.GetPropertyHeight(ShaderEditor.Active.PropertyDictionary[r_property].MaterialProperty);
+ }
+ }
+ if (DrawingData.CurrentTextureProperty.DoesReferencePropertyExist)
+ {
+ border.height += 8;
+ border.height += editor.GetPropertyHeight(ShaderEditor.Active.PropertyDictionary[DrawingData.CurrentTextureProperty.Options.reference_property].MaterialProperty);
+ }
+
+
+ //background
+ //GUI.DrawTexture(border, Styles.rounded_texture, ScaleMode.StretchToFill, true, 0, Styles.COLOR_BACKGROUND_1, 0, 0);
+
+ Color prevC = GUI.color;
+ GUI.color = Styles.COLOR_BACKGROUND_1;
+
+ GUI.DrawTexture(border, Styles.rounded_texture, ScaleMode.StretchToFill, true);
+ Rect quad = new Rect(border);
+ quad.width = quad.height / 2;
+ GUI.DrawTextureWithTexCoords(quad, Styles.rounded_texture, new Rect(0, 0, 0.5f, 1), true);
+ quad.x += border.width - quad.width;
+ GUI.DrawTextureWithTexCoords(quad, Styles.rounded_texture, new Rect(0.5f, 0, 0.5f, 1), true);
+
+ GUI.color = prevC;
+
+ quad.width = border.height - 4;
+ quad.height = quad.width;
+ quad.x = border.x + border.width - quad.width - 1;
+ quad.y += 2;
+
+ Rect preview_rect_border = new Rect(position);
+ preview_rect_border.height = rect.height + position.height - 6;
+ preview_rect_border.width = preview_rect_border.height;
+ preview_rect_border.y += 3;
+ preview_rect_border.x += position.width - preview_rect_border.width - 3;
+ Rect preview_rect = new Rect(preview_rect_border);
+ preview_rect.height -= 6;
+ preview_rect.width -= 6;
+ preview_rect.x += 3;
+ preview_rect.y += 3;
+ if (prop.hasMixedValue)
+ {
+ Rect mixedRect = new Rect(preview_rect);
+ mixedRect.y -= 5;
+ mixedRect.x += mixedRect.width / 2 - 4;
+ GUI.Label(mixedRect, "_");
+ }
+ else if (prop.textureValue != null)
+ {
+ GUI.DrawTexture(preview_rect, prop.textureValue);
+ }
+ GUI.DrawTexture(preview_rect_border, Texture2D.whiteTexture, ScaleMode.StretchToFill, false, 0, Color.grey, 3, 5);
+
+ //selection button and pinging
+ Rect select_rect = new Rect(preview_rect);
+ select_rect.height = 12;
+ select_rect.y += preview_rect.height - 12;
+ if (Event.current.commandName == "ObjectSelectorUpdated" && EditorGUIUtility.GetObjectPickerControlID() == s_texturePickerWindow && s_texturePickerWindowProperty.name == prop.name)
+ {
+ prop.textureValue = (Texture)EditorGUIUtility.GetObjectPickerObject();
+ ShaderEditor.RepaintActive();
+ }
+ if (Event.current.commandName == "ObjectSelectorClosed" && EditorGUIUtility.GetObjectPickerControlID() == s_texturePickerWindow)
+ {
+ s_texturePickerWindow = -1;
+ s_texturePickerWindowProperty = null;
+ }
+ if (GUI.Button(select_rect, "Select", EditorStyles.miniButton))
+ {
+ EditorGUIUtility.ShowObjectPicker<Texture>(prop.textureValue, false, "", 0);
+ s_texturePickerWindow = EditorGUIUtility.GetObjectPickerControlID();
+ s_texturePickerWindowProperty = prop;
+ }
+ else if (Event.current.type == EventType.MouseDown && preview_rect.Contains(Event.current.mousePosition))
+ {
+ EditorGUIUtility.PingObject(prop.textureValue);
+ }
+
+ if (!skip_drag_and_drop_handling)
+ if ((ShaderEditor.Input.is_drag_drop_event) && preview_rect.Contains(ShaderEditor.Input.mouse_position) && DragAndDrop.objectReferences[0] is Texture)
+ {
+ DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
+ if (ShaderEditor.Input.is_drop_event)
+ {
+ DragAndDrop.AcceptDrag();
+ prop.textureValue = (Texture)DragAndDrop.objectReferences[0];
+ }
+ }
+
+ //scale offset rect
+
+ if (hasFoldoutProperties || DrawingData.CurrentTextureProperty.Options.reference_property != null)
+ {
+ EditorGUI.indentLevel += 2;
+
+ if (DrawingData.CurrentTextureProperty.hasScaleOffset)
+ {
+ Rect scale_offset_rect = new Rect(position);
+ scale_offset_rect.y += 37;
+ scale_offset_rect.width -= 2 + preview_rect.width + 10 + 30;
+ scale_offset_rect.x += 30;
+ editor.TextureScaleOffsetProperty(scale_offset_rect, prop);
+ }
+
+ //In case of locked material end disabled group here to allow editing of sub properties
+ if (ShaderEditor.Active.IsLockedMaterial) EditorGUI.EndDisabledGroup();
+
+ float oldLabelWidth = EditorGUIUtility.labelWidth;
+ EditorGUIUtility.labelWidth = 128;
+
+ PropertyOptions options = DrawingData.CurrentTextureProperty.Options;
+ if (options.reference_property != null)
+ {
+ ShaderProperty property = ShaderEditor.Active.PropertyDictionary[options.reference_property];
+ property.Draw(useEditorIndent: true);
+ }
+ if (options.reference_properties != null)
+ foreach (string r_property in options.reference_properties)
+ {
+ ShaderProperty property = ShaderEditor.Active.PropertyDictionary[r_property];
+ property.Draw(useEditorIndent: true);
+ }
+ EditorGUIUtility.labelWidth = oldLabelWidth;
+ EditorGUI.indentLevel -= 2;
+
+ //readd disabled group
+ if (ShaderEditor.Active.IsLockedMaterial) EditorGUI.BeginDisabledGroup(false);
+ }
+
+ Rect label_rect = new Rect(position);
+ label_rect.x += 2;
+ label_rect.y += 2;
+ GUI.Label(label_rect, label);
+
+ GUILayoutUtility.GetRect(0, 5);
+
+ DrawingData.LastGuiObjectRect = border;
+ DrawingData.TooltipCheckRect = border;
+ }
+
+ static Stack<int> s_previousIndentLevels = new Stack<int>();
+ public static void BeginCustomIndentLevel(int indent)
+ {
+ s_previousIndentLevels.Push(EditorGUI.indentLevel);
+ EditorGUI.indentLevel = indent;
+ }
+
+ public static void EndCustomIndentLevel()
+ {
+ EditorGUI.indentLevel = s_previousIndentLevels.Pop();
+ }
+
+ static string[] s_fallbackShaderTypes = { "Standard", "Toon", "Unlit", "VertexLit", "Particle", "Sprite", "Matcap", "MobileToon" };
+ static string[] s_fallbackRenderTypes = { "Opaque", "Cutout", "Transparent", "Fade" };
+ static string[] s_fallbackRenderTypesValues = { "", "Cutout", "Transparent", "Fade" };
+ static string[] s_fallbackCullTypes = { "OneSided", "DoubleSided" };
+ static string[] s_fallbackCullTypesValues = { "", "DoubleSided" };
+ static string[] s_fallbackNoTypes = { "None", "Hidden" };
+ static string[] s_fallbackNoTypesValues = { "", "Hidden" };
+ static string[] s_vRCFallbackOptionsPopup = s_fallbackNoTypes.Union(s_fallbackShaderTypes.SelectMany(s => s_fallbackRenderTypes.SelectMany(r => s_fallbackCullTypes.Select(c => r + "/" + c).Select(rc => s + "/" + rc)))).ToArray();
+ static string[] s_vRCFallbackOptionsValues = s_fallbackNoTypes.Union(s_fallbackShaderTypes.SelectMany(s => s_fallbackRenderTypesValues.SelectMany(r => s_fallbackCullTypesValues.Select(c => r + c).Select(rc => s + rc)))).ToArray();
+ public static void VRCFallbackSelector(ShaderEditor shaderEditor)
+ {
+ string current = shaderEditor.Materials[0].GetTag("VRCFallback", false, "None");
+ EditorGUI.BeginChangeCheck();
+ int selected = EditorGUILayout.Popup("VRChat Fallback Shader", s_vRCFallbackOptionsValues.Select((f, i) => (f, i)).FirstOrDefault(f => f.f == current).i , s_vRCFallbackOptionsPopup);
+ if (EditorGUI.EndChangeCheck())
+ shaderEditor.Materials[0].SetOverrideTag("VRCFallback", s_vRCFallbackOptionsValues[selected]);
+ }
+
+ public static void MinMaxSlider(Rect settingsRect, GUIContent content, MaterialProperty prop)
+ {
+ bool changed = false;
+ Vector4 vec = prop.vectorValue;
+ Rect sliderRect = settingsRect;
+
+ EditorGUI.LabelField(settingsRect, content);
+
+ if (settingsRect.width > 160)
+ {
+ Rect numberRect = settingsRect;
+ numberRect.width = 65 + (EditorGUI.indentLevel - 1) * 15;
+
+ numberRect.x = EditorGUIUtility.labelWidth - (EditorGUI.indentLevel - 1) * 15;
+
+ EditorGUI.BeginChangeCheck();
+ EditorGUI.showMixedValue = prop.hasMixedValue;
+ vec.x = EditorGUI.FloatField(numberRect, vec.x, EditorStyles.textField);
+ changed |= EditorGUI.EndChangeCheck();
+
+ numberRect.x = settingsRect.xMax - numberRect.width;
+
+ EditorGUI.BeginChangeCheck();
+ EditorGUI.showMixedValue = prop.hasMixedValue;
+ vec.y = EditorGUI.FloatField(numberRect, vec.y);
+ changed |= EditorGUI.EndChangeCheck();
+
+ sliderRect.xMin = EditorGUIUtility.labelWidth - (EditorGUI.indentLevel - 1) * 15;
+ sliderRect.xMin += (65 + -8);
+ sliderRect.xMax -= (65 + -8);
+ }
+
+ vec.x = Mathf.Clamp(vec.x, vec.z, vec.y);
+ vec.y = Mathf.Clamp(vec.y, vec.x, vec.w);
+
+ EditorGUI.BeginChangeCheck();
+ EditorGUI.MinMaxSlider(sliderRect, ref vec.x, ref vec.y, vec.z, vec.w);
+ changed |= EditorGUI.EndChangeCheck();
+
+ if (changed)
+ {
+ prop.vectorValue = vec;
+ }
+ }
+
+ public static bool DrawListField<type>(List<type> list) where type : UnityEngine.Object
+ {
+ GUILayout.BeginHorizontal();
+ if (GUILayout.Button("Add", EditorStyles.miniButton))
+ list.Add(null);
+ if (GUILayout.Button("Remove", EditorStyles.miniButton))
+ if (list.Count > 0)
+ list.RemoveAt(list.Count - 1);
+ GUILayout.EndHorizontal();
+
+ for (int i = 0; i < list.Count; i++)
+ {
+ list[i] = (type)EditorGUILayout.ObjectField(list[i], typeof(type), false);
+ }
+ return false;
+ }
+ public static bool DrawListField<type>(List<type> list, float maxHeight, ref Vector2 scrollPosition) where type : UnityEngine.Object
+ {
+ GUILayout.BeginHorizontal();
+ if (GUILayout.Button("Add", EditorStyles.miniButton))
+ list.Add(null);
+ if (GUILayout.Button("Remove", EditorStyles.miniButton))
+ if (list.Count > 0)
+ list.RemoveAt(list.Count - 1);
+ GUILayout.EndHorizontal();
+
+ scrollPosition = GUILayout.BeginScrollView(scrollPosition, GUILayout.MaxHeight(maxHeight));
+ for (int i = 0; i < list.Count; i++)
+ {
+ list[i] = (type)EditorGUILayout.ObjectField(list[i], typeof(type), false);
+ }
+ GUILayout.EndScrollView();
+ return false;
+ }
+
+ public static bool GUIDataStruct<t>(t data)
+ {
+ return GUIDataStruct<t>(data, new string[] { });
+ }
+
+ public static bool GUIDataStruct<t>(t data, string[] exclude)
+ {
+ Type type = data.GetType();
+ bool changed = false;
+ foreach (FieldInfo f in type.GetFields())
+ {
+ bool skip = false;
+ foreach (string s in exclude)
+ if (s == f.Name)
+ skip = true;
+ if (skip)
+ continue;
+
+ if (f.FieldType.IsEnum)
+ changed |= GUIEnum(f, data);
+ else if (f.FieldType == typeof(string))
+ changed |= GUIString(f, data);
+ else if (f.FieldType == typeof(int))
+ changed |= GUIInt(f, data);
+ else if (f.FieldType == typeof(float))
+ changed |= GUIFloat(f, data);
+ }
+ return changed;
+ }
+
+ private static bool GUIEnum(FieldInfo f, object o)
+ {
+ EditorGUI.BeginChangeCheck();
+ Enum e = EditorGUILayout.EnumPopup(f.Name, (Enum)f.GetValue(o), GUILayout.ExpandWidth(false));
+ bool changed = EditorGUI.EndChangeCheck();
+ if (changed)
+ f.SetValue(o, e);
+ return changed;
+ }
+
+ private static bool GUIString(FieldInfo f, object o)
+ {
+ EditorGUI.BeginChangeCheck();
+ string s = EditorGUILayout.TextField(f.Name, (string)f.GetValue(o), GUILayout.ExpandWidth(false));
+ bool changed = EditorGUI.EndChangeCheck();
+ if (changed)
+ f.SetValue(o, s);
+ return changed;
+ }
+
+ private static bool GUIInt(FieldInfo f, object o)
+ {
+ EditorGUI.BeginChangeCheck();
+ int i = EditorGUILayout.IntField(f.Name, (int)f.GetValue(o), GUILayout.ExpandWidth(false));
+ bool changed = EditorGUI.EndChangeCheck();
+ if (changed)
+ f.SetValue(o, i);
+ return changed;
+ }
+
+ private static bool GUIFloat(FieldInfo f, object o)
+ {
+ EditorGUI.BeginChangeCheck();
+ float i = EditorGUILayout.FloatField(f.Name, (float)f.GetValue(o), GUILayout.ExpandWidth(false));
+ bool changed = EditorGUI.EndChangeCheck();
+ if (changed)
+ f.SetValue(o, i);
+ return changed;
+ }
+
+ public static void DrawLocaleSelection(GUIContent label, string[] locales, int selected)
+ {
+ EditorGUI.BeginChangeCheck();
+ selected = EditorGUILayout.Popup(label.text, selected, locales);
+ if (EditorGUI.EndChangeCheck())
+ {
+ ShaderEditor.Active.PropertyDictionary[ShaderEditor.PROPERTY_NAME_LOCALE].MaterialProperty.floatValue = selected;
+ ShaderEditor.ReloadActive();
+ }
+ }
+
+ public static void DrawHeader(ref bool enabled, ref bool options, GUIContent name)
+ {
+ var r = EditorGUILayout.BeginHorizontal("box");
+ enabled = EditorGUILayout.Toggle(enabled, EditorStyles.radioButton, GUILayout.MaxWidth(15.0f));
+ options = GUI.Toggle(r, options, GUIContent.none, new GUIStyle());
+ EditorGUILayout.LabelField(name, Styles.dropDownHeaderLabel);
+ EditorGUILayout.EndHorizontal();
+ }
+
+ public static void DrawMasterLabel(string shaderName, Rect parent)
+ {
+ Rect rect = new Rect(0, parent.y, parent.width, 18);
+ EditorGUI.LabelField(rect, "<size=16>" + shaderName + "</size>", Styles.masterLabel);
+ }
+
+ public static float CurrentIndentWidth()
+ {
+ return EditorGUI.indentLevel * 15;
+ }
+ // Mimics the normal map import warning - written by Orels1
+ static bool TextureImportWarningBox(string message){
+ GUILayout.BeginVertical(new GUIStyle(EditorStyles.helpBox));
+ EditorGUILayout.LabelField(message, new GUIStyle(EditorStyles.label) {
+ fontSize = 9, wordWrap = true
+ });
+ EditorGUILayout.BeginHorizontal(new GUIStyle() {
+ alignment = TextAnchor.MiddleRight
+ }, GUILayout.Height(24));
+ EditorGUILayout.Space();
+ bool buttonPress = GUILayout.Button("Fix Now", new GUIStyle("button") {
+ stretchWidth = false,
+ margin = new RectOffset(0, 0, 0, 0),
+ padding = new RectOffset(9, 9, 0, 0)
+ }, GUILayout.Height(22));
+ EditorGUILayout.EndHorizontal();
+ GUILayout.EndVertical();
+ return buttonPress;
+ }
+ public static void sRGBWarning(MaterialProperty tex){
+ if (tex.textureValue){
+ string sRGBWarning = "This texture is marked as sRGB, but should not contain color information.";
+ string texPath = AssetDatabase.GetAssetPath(tex.textureValue);
+ TextureImporter texImporter;
+ var importer = TextureImporter.GetAtPath(texPath) as TextureImporter;
+ if (importer != null){
+ texImporter = (TextureImporter)importer;
+ if (texImporter.sRGBTexture){
+ if (TextureImportWarningBox(sRGBWarning)){
+ texImporter.sRGBTexture = false;
+ texImporter.SaveAndReimport();
+ }
+ }
+ }
+ }
+ }
+
+ public static bool Button(Rect r, GUIStyle style)
+ {
+ return GUI.Button(r, GUIContent.none, style);
+ }
+
+ public static bool Button(Rect r, string tooltip, GUIStyle style)
+ {
+ return GUI.Button(r, new GUIContent("", tooltip), style);
+ }
+
+ public static bool Button(GUIStyle style, int width, int height)
+ {
+ Rect r = GUILayoutUtility.GetRect(width, height);
+ return Button(r, style);
+ }
+
+ public static bool ButtonWithCursor(GUIStyle style, int width, int height)
+ {
+ Rect r = GUILayoutUtility.GetRect(width, height);
+ EditorGUIUtility.AddCursorRect(r, MouseCursor.Link);
+ return Button(r, style);
+ }
+
+ public static bool ButtonWithCursor(GUIStyle style, string tooltip, int width, int height)
+ {
+ Rect r = GUILayoutUtility.GetRect(width, height);
+ EditorGUIUtility.AddCursorRect(r, MouseCursor.Link);
+ return Button(r, tooltip, style);
+ }
+
+ public static bool ButtonWithCursor(GUIStyle style, string tooltip, int width, int height, out Rect r)
+ {
+ r = GUILayoutUtility.GetRect(width, height);
+ EditorGUIUtility.AddCursorRect(r, MouseCursor.Link);
+ return Button(r, tooltip, style);
+ }
+
+ public static bool Button(Rect r, string tooltip, GUIStyle style, Color c)
+ {
+ Color prevColor = GUI.backgroundColor;
+ GUI.backgroundColor = c;
+ bool b = GuiHelper.Button(r, tooltip, style);
+ GUI.backgroundColor = prevColor;
+ return b;
+ }
+
+ public static bool Button(GUIStyle style, int width, int height, Color c)
+ {
+ Color prevColor = GUI.backgroundColor;
+ GUI.backgroundColor = c;
+ bool b = GuiHelper.Button(style, width, height);
+ GUI.backgroundColor = prevColor;
+ return b;
+ }
+
+ public static bool Button(Rect r, GUIStyle style, Color c, bool doColor)
+ {
+ Color prevColor = GUI.backgroundColor;
+ if(doColor) GUI.backgroundColor = c;
+ bool b = GuiHelper.Button(r, style);
+ GUI.backgroundColor = prevColor;
+ return b;
+ }
+
+ #region SearchableEnumPopup
+ public class SearchableEnumPopup : EditorWindow
+ {
+ private static SearchableEnumPopup window;
+ public static void CreateSearchableEnumPopup(string[] options, string selected, Action<string> setter)
+ {
+ Vector2 pos = GUIUtility.GUIToScreenPoint(Event.current.mousePosition);
+ pos.x = Mathf.Min(EditorWindow.focusedWindow.position.x + EditorWindow.focusedWindow.position.width - 250, pos.x);
+ pos.y = Mathf.Min(EditorWindow.focusedWindow.position.y + EditorWindow.focusedWindow.position.height - 200, pos.y);
+
+ if (window != null)
+ window.Close();
+ window = ScriptableObject.CreateInstance<SearchableEnumPopup>();
+ window.position = new Rect(pos.x, pos.y, 250, 200);
+ window._options = options;
+ window._selected = selected;
+ window._setter = setter;
+ window._searchedFor = "";
+ window.ShowPopup();
+ }
+
+ private SearchableEnumPopup() { }
+
+ string[] _options;
+ string _selected;
+ string _searchedFor;
+ Action<string> _setter;
+
+ bool first = true;
+
+ private void OnGUI()
+ {
+ if (GUILayout.Button("Close")) this.Close();
+ GUI.SetNextControlName("SearchTextField");
+ _searchedFor = GUILayout.TextField(_searchedFor);
+ string seachTerm = _searchedFor.ToLowerInvariant().TrimStart('_');
+ string[] filteredOptions = _options.Where(o => o.TrimStart('_').ToLowerInvariant().StartsWith(seachTerm)).ToArray();
+ for (int i = 0; i < 7 && i < filteredOptions.Length; i++)
+ {
+ if (GUILayout.Button(filteredOptions[i]))
+ {
+ _selected = filteredOptions[i];
+ _setter.Invoke(_selected);
+ this.Close();
+ }
+ }
+ if (filteredOptions.Length > 7)
+ {
+ GUILayout.Label("... More");
+ }
+ if (first)
+ {
+ GUI.FocusControl("SearchTextField");
+ first = false;
+ }
+ }
+ }
+
+ #endregion
+ }
+
+ public class BetterTooltips
+ {
+ private static Tooltip activeTooltip;
+
+ public class Tooltip
+ {
+ private GUIContent content;
+ private bool empty;
+
+ public bool isSelected { get; private set; } = false;
+
+ private Rect containerRect;
+ private Rect contentRect;
+
+ readonly static Vector2 PADDING = new Vector2(10, 10);
+
+ public Tooltip(string text)
+ {
+ content = new GUIContent(text);
+ empty = string.IsNullOrWhiteSpace(text);
+ }
+
+ public Tooltip(string text, Texture texture)
+ {
+ content = new GUIContent(text, texture);
+ empty = string.IsNullOrWhiteSpace(text) && texture == null;
+ }
+
+ public void SetText(string text)
+ {
+ content.text = text;
+ empty &= string.IsNullOrWhiteSpace(text);
+ }
+
+ public void ConditionalDraw(Rect hoverOverRect)
+ {
+ if (empty) return;
+ bool isSelected = hoverOverRect.Contains(Event.current.mousePosition);
+ if (isSelected )
+ {
+ CalculatePositions(hoverOverRect);
+ activeTooltip = this;
+ this.isSelected = true;
+ }
+ }
+
+ private void CalculatePositions(Rect hoverOverRect)
+ {
+ Vector2 contentSize = EditorStyles.label.CalcSize(content);
+ Vector2 containerPosition = new Vector2(Event.current.mousePosition.x - contentSize.x / 2 - PADDING.x / 2, hoverOverRect.y - contentSize.y - PADDING.y - 3);
+
+ containerPosition.x = Mathf.Max(0, containerPosition.x);
+ containerPosition.x = Mathf.Min(EditorGUIUtility.currentViewWidth - contentSize.x - PADDING.x, containerPosition.x);
+
+ contentRect = new Rect(containerPosition + new Vector2(PADDING.x/2, PADDING.y/2), contentSize);
+ containerRect = new Rect(containerPosition, contentSize + new Vector2(PADDING.x, PADDING.y));
+ }
+
+ public void Draw()
+ {
+ EditorGUI.DrawRect(containerRect, Styles.COLOR_BG);
+ EditorGUI.LabelField(contentRect, content);
+ isSelected = false;
+ }
+ }
+
+ public static void DrawActive()
+ {
+ if(activeTooltip != null)
+ {
+ if (activeTooltip.isSelected)
+ {
+ activeTooltip.Draw();
+ }
+ else
+ {
+ activeTooltip = null;
+ }
+ }
+ }
+ }
+
+ public class FooterButton
+ {
+ private GUIContent content;
+ private bool isTextureContent;
+ const int texture_height = 40;
+ int texture_width;
+ private ButtonData data;
+
+ public FooterButton(ButtonData data)
+ {
+ this.data = data;
+ if (data != null)
+ {
+ if (data.texture == null)
+ {
+ content = new GUIContent(data.text, data.hover);
+ isTextureContent = false;
+ }
+ else
+ {
+ texture_width = (int)((float)data.texture.loaded_texture.width / data.texture.loaded_texture.height * texture_height);
+ content = new GUIContent(data.texture.loaded_texture, data.hover);
+ isTextureContent = true;
+ }
+ }
+ else
+ {
+ content = new GUIContent();
+ }
+ }
+
+ public void Draw()
+ {
+ Rect cursorRect;
+ if (isTextureContent)
+ {
+ if(GUILayout.Button(content, new GUIStyle(), GUILayout.MaxWidth(texture_width), GUILayout.Height(texture_height))){
+ data.action.Perform();
+ }
+ cursorRect = GUILayoutUtility.GetLastRect();
+ GUILayout.Space(8);
+ }
+ else
+ {
+ if (GUILayout.Button(content, GUILayout.ExpandWidth(false), GUILayout.Height(texture_height)))
+ data.action.Perform();
+ cursorRect = GUILayoutUtility.GetLastRect();
+ GUILayout.Space(2);
+ }
+ EditorGUIUtility.AddCursorRect(cursorRect, MouseCursor.Link);
+ }
+
+ public static void DrawList(List<FooterButton> list)
+ {
+ EditorGUILayout.BeginHorizontal();
+ GUILayout.FlexibleSpace();
+ GUILayout.Space(2);
+ foreach (FooterButton b in list)
+ {
+ b.Draw();
+ }
+ GUILayout.FlexibleSpace();
+ EditorGUILayout.EndHorizontal();
+ }
+ }
+
+ public class ThryHeaderDrawer : MaterialPropertyDrawer
+ {
+ private MaterialProperty property;
+
+ private bool expanded;
+
+ private string keyword;
+ private string end;
+
+ public bool isHideable;
+
+ int p_xOffset;
+ int p_xOffset_total;
+ public int xOffset
+ {
+ set
+ {
+ p_xOffset = value;
+ p_xOffset_total = value * 15 + 15;
+ }
+ }
+
+ private ButtonData button;
+
+ public ThryHeaderDrawer(string end, string keyword, string buttonText, string buttonHover, string buttonAction, float isHideable)
+ {
+ this.end = end;
+ this.keyword = keyword;
+
+ button = new ButtonData();
+ button.text = buttonText;
+ button.hover = buttonHover;
+ button.action = DefineableAction.ParseDrawerParameter(buttonAction);
+
+ this.isHideable = isHideable == 1;
+ }
+
+ public ThryHeaderDrawer(string end, string keyword, string buttonText, string buttonHover, string buttonAction) : this(end, keyword, buttonText, buttonHover, buttonAction, 0 ) { }
+ public ThryHeaderDrawer(string end, string keyword, float isHideable) : this(end, keyword, null, null, null, isHideable ) { }
+ public ThryHeaderDrawer(string end, string keyword, string buttonAction) : this(end, keyword, null, null, buttonAction, 0 ) { }
+ public ThryHeaderDrawer(string end, string keyword) : this(end, keyword, null, null, null, 0 ) { }
+ public ThryHeaderDrawer(string end) : this(end, null , null, null, null, 0 ) { }
+
+ public ThryHeaderDrawer(float isHideable) : this(null,null, null, null, null, isHideable ) { }
+ public ThryHeaderDrawer(float isHideable, string end) : this(end, null, null, null, null, isHideable ) { }
+ public ThryHeaderDrawer(float isHideable, string buttonText, string buttonHover, string buttonAction) : this(null,null, buttonText, buttonHover, buttonAction, 0 ) { }
+ public ThryHeaderDrawer(float isHideable, string end, string buttonText, string buttonHover, string buttonAction):this(end, null, buttonText, buttonHover, buttonAction, isHideable ) { }
+
+ public ThryHeaderDrawer(){}
+
+ public string GetEndProperty()
+ {
+ return end;
+ }
+
+ public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
+ {
+ DrawingData.LastPropertyUsedCustomDrawer = true;
+ DrawingData.LastPropertyDrawerType = DrawerType.Header;
+ DrawingData.LastPropertyDrawer = this;
+ return base.GetPropertyHeight(prop, label, editor);
+ }
+
+ public bool is_expanded
+ {
+ get
+ {
+ return expanded;
+ }
+ }
+
+ public void Toggle()
+ {
+ expanded = !expanded;
+ foreach (Material m in ShaderEditor.Active.Materials) m.SetFloat(property.name, expanded ? 1 : 0);
+ }
+
+ public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
+ {
+ if (this.property == null)
+ {
+ this.property = prop;
+ this.expanded = prop.floatValue == 1;
+ }
+
+ PropertyOptions options = ShaderEditor.Active.CurrentProperty.Options;
+ Event e = Event.current;
+
+ position.width -= p_xOffset_total - position.x;
+ position.x = p_xOffset_total;
+
+ DrawingData.LastGuiObjectHeaderRect = position;
+
+ DrawBoxAndContent(position, e, label, options);
+
+ DrawSmallArrow(position, e);
+ HandleToggleInput(position);
+ }
+
+ private void DrawBoxAndContent(Rect rect, Event e, GUIContent content, PropertyOptions options)
+ {
+ if (options.reference_property != null && ShaderEditor.Active.PropertyDictionary.ContainsKey(options.reference_property))
+ {
+ GUI.Box(rect, new GUIContent(" " + content.text, content.tooltip), Styles.dropDownHeader);
+ DrawIcons(rect, options, e);
+
+ Rect togglePropertyRect = new Rect(rect);
+ togglePropertyRect.x += 5;
+ togglePropertyRect.y += 1;
+ togglePropertyRect.height -= 4;
+ togglePropertyRect.width = GUI.skin.font.fontSize * 3;
+ float fieldWidth = EditorGUIUtility.fieldWidth;
+ EditorGUIUtility.fieldWidth = 20;
+ ShaderProperty prop = ShaderEditor.Active.PropertyDictionary[options.reference_property];
+
+ int xOffset = prop.XOffset;
+ prop.XOffset = 0;
+ prop.Draw(new CRect(togglePropertyRect), new GUIContent(), isInHeader: true);
+ prop.XOffset = xOffset;
+ EditorGUIUtility.fieldWidth = fieldWidth;
+ }else if(keyword != null)
+ {
+ GUI.Box(rect, " " + content.text, Styles.dropDownHeader);
+ DrawIcons(rect, options, e);
+
+ Rect togglePropertyRect = new Rect(rect);
+ togglePropertyRect.x += 20;
+ togglePropertyRect.width = 20;
+
+ EditorGUI.BeginChangeCheck();
+ bool keywordOn = EditorGUI.Toggle(togglePropertyRect, "", ShaderEditor.Active.Materials[0].IsKeywordEnabled(keyword));
+ if (EditorGUI.EndChangeCheck())
+ {
+ MaterialHelper.ToggleKeyword(ShaderEditor.Active.Materials, keyword, keywordOn);
+ }
+ }
+ else
+ {
+ GUI.Box(rect, content, Styles.dropDownHeader);
+ DrawIcons(rect, options, e);
+ }
+
+ }
+
+ /// <summary>
+ /// Draws the icons for ShaderEditor features like linking and copying
+ /// </summary>
+ /// <param name="rect"></param>
+ /// <param name="e"></param>
+ private void DrawIcons(Rect rect, PropertyOptions options, Event e)
+ {
+ Rect buttonRect = new Rect(rect);
+ buttonRect.y += 1;
+ buttonRect.height -= 4;
+ buttonRect.width = buttonRect.height;
+
+ float right = rect.x + rect.width;
+ buttonRect.x = right - 56;
+ DrawHelpButton(buttonRect, options, e);
+ buttonRect.x = right - 38;
+ DrawLinkSettings(buttonRect, e);
+ buttonRect.x = right - 20;
+ DrawDowdownSettings(buttonRect, e);
+ }
+
+ private void DrawHelpButton(Rect rect, PropertyOptions options, Event e)
+ {
+ ButtonData button = this.button != null ? this.button : options.button_help;
+ if (button != null && button.condition_show.Test())
+ {
+ if (GuiHelper.Button(rect, Styles.icon_style_help))
+ {
+ ShaderEditor.Input.Use();
+ if (button.action != null) button.action.Perform();
+ }
+ }
+ }
+
+ private void DrawDowdownSettings(Rect rect, Event e)
+ {
+ if (GuiHelper.Button(rect, Styles.icon_style_menu))
+ {
+ ShaderEditor.Input.Use();
+ Rect buttonRect = new Rect(rect);
+ buttonRect.width = 150;
+ buttonRect.x = Mathf.Min(Screen.width - buttonRect.width, buttonRect.x);
+ buttonRect.height = 60;
+ float maxY = GUIUtility.ScreenToGUIPoint(new Vector2(0, EditorWindow.focusedWindow.position.y + Screen.height)).y - 2.5f * buttonRect.height;
+ buttonRect.y = Mathf.Min(buttonRect.y - buttonRect.height / 2, maxY);
+
+ ShowHeaderContextMenu(buttonRect, ShaderEditor.Active.CurrentProperty, ShaderEditor.Active.Materials[0]);
+ }
+ }
+
+ private void DrawLinkSettings(Rect rect, Event e)
+ {
+ if (GuiHelper.Button(rect, Styles.icon_style_linked, Styles.COLOR_ICON_ACTIVE_CYAN, MaterialLinker.IsLinked(ShaderEditor.Active.CurrentProperty.MaterialProperty)))
+ {
+ ShaderEditor.Input.Use();
+ List<Material> linked_materials = MaterialLinker.GetLinked(ShaderEditor.Active.CurrentProperty.MaterialProperty);
+ MaterialLinker.Popup(rect, linked_materials, ShaderEditor.Active.CurrentProperty.MaterialProperty);
+ }
+ }
+
+ void ShowHeaderContextMenu(Rect position, ShaderPart property, Material material)
+ {
+ var menu = new GenericMenu();
+ menu.AddItem(new GUIContent("Reset"), false, delegate ()
+ {
+ property.CopyFromMaterial(new Material(material.shader), true);
+ List<Material> linked_materials = MaterialLinker.GetLinked(property.MaterialProperty);
+ if (linked_materials != null)
+ foreach (Material m in linked_materials)
+ property.CopyToMaterial(m, true);
+ });
+ menu.AddItem(new GUIContent("Copy"), false, delegate ()
+ {
+ Mediator.copy_material = new Material(material);
+ Mediator.transfer_group = property;
+ });
+ menu.AddItem(new GUIContent("Paste"), false, delegate ()
+ {
+ if (Mediator.copy_material != null || Mediator.transfer_group != null)
+ {
+ property.TransferFromMaterialAndGroup(Mediator.copy_material, Mediator.transfer_group, true);
+ List<Material> linked_materials = MaterialLinker.GetLinked(property.MaterialProperty);
+ if (linked_materials != null)
+ foreach (Material m in linked_materials)
+ property.CopyToMaterial(m, true);
+ }
+ });
+ menu.DropDown(position);
+ }
+
+ private void DrawSmallArrow(Rect rect, Event e)
+ {
+ if (e.type == EventType.Repaint)
+ {
+ var toggleRect = new Rect(rect.x + 4f, rect.y + 2f, 13f, 13f);
+ EditorStyles.foldout.Draw(toggleRect, false, false, expanded, false);
+ }
+ }
+
+ private void HandleToggleInput(Rect rect)
+ {
+ //Ignore unity uses is cause disabled will use the event to prevent toggling
+ if (ShaderEditor.Input.LeftClick_IgnoreLocked && rect.Contains(ShaderEditor.Input.mouse_position) && !ShaderEditor.Input.is_alt_down)
+ {
+ this.Toggle();
+ ShaderEditor.Input.Use();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/GUIHelper.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/GUIHelper.cs.meta
new file mode 100644
index 00000000..56ae27f6
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/GUIHelper.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 86ee0d31fc0c2e740bb6237d6281a257
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/GradiantEditor.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/GradiantEditor.cs
new file mode 100644
index 00000000..8c5cd52b
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/GradiantEditor.cs
@@ -0,0 +1,258 @@
+// Material/Shader Inspector for Unity 2017/2018
+// Copyright (C) 2019 Thryrallo
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Text.RegularExpressions;
+using UnityEditor;
+using UnityEngine;
+
+namespace Thry
+{
+ public class GradientEditor : EditorWindow
+ {
+
+ public static void Open(GradientData data, MaterialProperty prop, TextureData predefinedTextureSettings, bool force_texture_options = false, bool show_texture_options=true)
+ {
+ texture_settings_data = LoadTextureSettings(prop, predefinedTextureSettings, force_texture_options);
+ data.Gradient = TextureHelper.GetGradient(prop.textureValue);
+ GradientEditor window = (GradientEditor)EditorWindow.GetWindow(typeof(GradientEditor));
+ window.privious_preview_texture = prop.textureValue;
+ window.prop = prop;
+ window.data = data;
+ window.show_texture_options = show_texture_options;
+ window.minSize = new Vector2(350, 350);
+ window.Show();
+ }
+
+ GradientData data;
+ MaterialProperty prop;
+
+ object gradient_editor;
+ MethodInfo ongui;
+ MethodInfo gradient_editor_init;
+
+ object preset_libary_editor;
+ MethodInfo preset_libary_onGUI;
+ object preset_libary_editor_state;
+
+ private bool inited = false;
+
+ private bool show_texture_options = true;
+
+ private bool gradient_has_been_edited = false;
+ private Texture privious_preview_texture;
+
+ private static TextureData LoadTextureSettings(MaterialProperty prop, TextureData predefinedTextureSettings, bool force_texture_options)
+ {
+ if (force_texture_options && predefinedTextureSettings != null)
+ return predefinedTextureSettings;
+ string json_texture_settings = FileHelper.LoadValueFromFile("gradient_texture_options_"+prop.name, PATH.PERSISTENT_DATA);
+ if (json_texture_settings != null)
+ return Parser.ParseToObject<TextureData>(json_texture_settings);
+ else if (predefinedTextureSettings != null)
+ return predefinedTextureSettings;
+ else
+ return new TextureData();
+ }
+ private static TextureData texture_settings_data;
+ private TextureData textureSettings
+ {
+ get
+ {
+ return texture_settings_data;
+ }
+ }
+
+ public void Awake()
+ {
+ Type gradient_editor_type = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.GradientEditor");
+ gradient_editor = Activator.CreateInstance(gradient_editor_type);
+ gradient_editor_init = gradient_editor_type.GetMethod("Init");
+
+ ongui = gradient_editor_type.GetMethod("OnGUI");
+ }
+
+ public void OnDestroy()
+ {
+ if (gradient_has_been_edited)
+ {
+ if (data.PreviewTexture.GetType() == typeof(Texture2D))
+ {
+ string file_name = GradientFileName(data.Gradient, prop.targets[0].name);
+ Texture saved = TextureHelper.SaveTextureAsPNG((Texture2D)data.PreviewTexture, PATH.TEXTURES_DIR+"/Gradients/" + file_name, textureSettings);
+ file_name = Regex.Replace(file_name, @"\.((png)|(jpg))$", "");
+ FileHelper.SaveValueToFile(AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(saved)), Parser.ObjectToString(data.Gradient), PATH.GRADIENT_INFO_FILE);
+ prop.textureValue = saved;
+ }
+ }
+ else
+ {
+ UpdatePreviewTexture(privious_preview_texture);
+ }
+ }
+
+ private string GradientFileName(Gradient gradient, string material_name)
+ {
+ string hash = "" + gradient.GetHashCode();
+ return GradientFileName(hash, material_name);
+ }
+
+ private string GradientFileName(string hash, string material_name)
+ {
+ Config config = Config.Singleton;
+ string ret = config.gradient_name;
+ ret = Regex.Replace(ret, "<hash>", hash);
+ ret = Regex.Replace(ret, "<material>", material_name);
+ return ret;
+ }
+
+ private void InitSomeStuff()
+ {
+ Type presetLibraryEditorState_type = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.PresetLibraryEditorState");
+ preset_libary_editor_state = Activator.CreateInstance(presetLibraryEditorState_type, "Gradient");
+ MethodInfo transfer_editor_prefs_state = presetLibraryEditorState_type.GetMethod("TransferEditorPrefsState");
+ transfer_editor_prefs_state.Invoke(preset_libary_editor_state, new object[] { true });
+
+ Type scriptable_save_load_helper_type = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.ScriptableObjectSaveLoadHelper`1");
+ Type gradient_preset_libary_type = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.GradientPresetLibrary");
+ Type preset_libary_editor_type = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.PresetLibraryEditor`1");
+ Type save_load_helper_type = scriptable_save_load_helper_type.MakeGenericType(gradient_preset_libary_type);
+ Type gradient_preset_libary_editor_type = preset_libary_editor_type.MakeGenericType(gradient_preset_libary_type);
+
+ object saveLoadHelper = Activator.CreateInstance(save_load_helper_type, "gradients", SaveType.Text);
+
+ Action<int, object> preset_libary_editor_callback = PresetClickedCallback;
+ preset_libary_editor = Activator.CreateInstance(gradient_preset_libary_editor_type, saveLoadHelper, preset_libary_editor_state, preset_libary_editor_callback);
+ PropertyInfo show_header = gradient_preset_libary_editor_type.GetProperty("showHeader");
+ show_header.SetValue(preset_libary_editor, true, null);
+ PropertyInfo minMaxPreviewHeight = gradient_preset_libary_editor_type.GetProperty("minMaxPreviewHeight");
+ minMaxPreviewHeight.SetValue(preset_libary_editor, new Vector2(14f, 14f), null);
+
+ preset_libary_onGUI = gradient_preset_libary_editor_type.GetMethod("OnGUI");
+
+ SetGradient(data.Gradient);
+ gradient_has_been_edited = false;
+
+ inited = true;
+ }
+
+ public void PresetClickedCallback(int clickCount, object presetObject)
+ {
+ Gradient gradient = presetObject as Gradient;
+ if (gradient == null)
+ Debug.LogError("Incorrect object passed " + presetObject);
+ SetGradient(gradient);
+ }
+
+ void SetGradient(Gradient gradient)
+ {
+ data.Gradient = gradient;
+#if UNITY_2020_1_OR_NEWER
+ gradient_editor_init.Invoke(gradient_editor, new object[] { gradient, 0, true, ColorSpace.Linear });
+#else
+ gradient_editor_init.Invoke(gradient_editor, new object[] { gradient, 0, true });
+#endif
+ UpdateGradientPreviewTexture();
+ }
+
+ void OnGUI()
+ {
+ if (!inited)
+ InitSomeStuff();
+ float gradientEditorHeight = Mathf.Min(position.height, 146);
+ float distBetween = 10f;
+ float presetLibraryHeight = Mathf.Min(position.height - gradientEditorHeight - distBetween-135,130);
+
+ Rect gradientEditorRect = new Rect(10, 10, position.width - 20, gradientEditorHeight - 20);
+ Rect gradientLibraryRect = new Rect(0, gradientEditorHeight + distBetween, position.width, presetLibraryHeight);
+
+ EditorGUI.BeginChangeCheck();
+ ongui.Invoke(gradient_editor, new object[] { gradientEditorRect });
+ if (EditorGUI.EndChangeCheck())
+ UpdateGradientPreviewTexture();
+
+ OverrideGradientTexture(gradientEditorRect);
+
+ preset_libary_onGUI.Invoke(preset_libary_editor, new object[] { gradientLibraryRect, data.Gradient });
+
+ GUILayout.BeginVertical();
+ GUILayout.Space(gradientEditorHeight+ presetLibraryHeight+ distBetween);
+ GUILayout.EndVertical();
+
+ GUILayout.BeginHorizontal();
+ GUILayout.FlexibleSpace();
+ if(GUILayout.Button("Discard Changes",GUILayout.ExpandWidth(false)))
+ DiscardChanges();
+ GUILayout.EndHorizontal();
+ if(show_texture_options)
+ TextureSettingsGUI();
+ }
+
+ private void DiscardChanges()
+ {
+ prop.textureValue = privious_preview_texture;
+ SetGradient(TextureHelper.GetGradient(privious_preview_texture));
+ gradient_has_been_edited = false;
+ ShaderEditor.RepaintActive();
+ }
+
+ private void TextureSettingsGUI()
+ {
+ EditorGUIUtility.labelWidth = 100;
+ EditorGUIUtility.fieldWidth = 150;
+ EditorGUILayout.LabelField("Texture options:",EditorStyles.boldLabel);
+ bool changed = GuiHelper.GUIDataStruct<TextureData>(textureSettings, new string[]{"name"});
+ if (changed)
+ {
+ FileHelper.SaveValueToFile("gradient_texture_options_" + prop.name, Parser.ObjectToString(textureSettings), PATH.PERSISTENT_DATA);
+ UpdateGradientPreviewTexture();
+ }
+ }
+
+ private void UpdateGradientPreviewTexture()
+ {
+ data.PreviewTexture = Converter.GradientToTexture(data.Gradient, textureSettings.width, textureSettings.height);
+ textureSettings.ApplyModes(data.PreviewTexture);
+ prop.textureValue = data.PreviewTexture;
+ gradient_has_been_edited = true;
+ ShaderEditor.RepaintActive();
+ }
+
+ private void UpdatePreviewTexture(Texture texture)
+ {
+ data.PreviewTexture = texture;
+ prop.textureValue = texture;
+ ShaderEditor.RepaintActive();
+ }
+
+ private void OverrideGradientTexture(Rect position)
+ {
+ Rect gradient_texture_position = new Rect(position);
+
+ float modeHeight = 24f;
+ float swatchHeight = 16f;
+ float editSectionHeight = 26f;
+ float gradientTextureHeight = gradient_texture_position.height - 2 * swatchHeight - editSectionHeight - modeHeight;
+ gradient_texture_position.y += modeHeight;
+ gradient_texture_position.y += swatchHeight;
+ gradient_texture_position.height = gradientTextureHeight;
+
+
+ Rect r2 = new Rect(gradient_texture_position.x + 1, gradient_texture_position.y + 1, gradient_texture_position.width - 2, gradient_texture_position.height - 2);
+
+ Texture2D backgroundTexture = TextureHelper.GetBackgroundTexture();
+ Rect texCoordsRect = new Rect(0, 0, r2.width / backgroundTexture.width, r2.height / backgroundTexture.height);
+ GUI.DrawTextureWithTexCoords(r2, backgroundTexture, texCoordsRect, false);
+
+ TextureWrapMode wrap_mode = data.PreviewTexture.wrapMode;
+ data.PreviewTexture.wrapMode = TextureWrapMode.Clamp;
+ GUI.DrawTexture(r2, data.PreviewTexture, ScaleMode.StretchToFill, true);
+ GUI.DrawTexture(gradient_texture_position, data.PreviewTexture, ScaleMode.StretchToFill, false, 0, Color.grey, 1, 1);
+ data.PreviewTexture.wrapMode = wrap_mode;
+ }
+
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/GradiantEditor.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/GradiantEditor.cs.meta
new file mode 100644
index 00000000..c36b6f71
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/GradiantEditor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 73c53de23fe449f41b1272569a42147a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Helper.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Helper.cs
new file mode 100644
index 00000000..a505fb36
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Helper.cs
@@ -0,0 +1,1679 @@
+// Material/Shader Inspector for Unity 2017/2018
+// Copyright (C) 2019 Thryrallo
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Net.Security;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using System.Text.RegularExpressions;
+using UnityEditor;
+using UnityEngine;
+using UnityEngine.Networking;
+
+namespace Thry
+{
+ static class StringExtensions
+ {
+ public static string ReplaceVariables(this string s, params object[] values)
+ {
+ for(int i = 0; i < values.Length;i++)
+ {
+ s = s.Replace("{" + i + "}", values[i].ToString());
+ }
+ return s;
+ }
+ }
+
+ public class Helper
+ {
+ static bool s_didTryRegsiterThisSession = false;
+
+ public static bool ClassWithNamespaceExists(string classname)
+ {
+ return (from assembly in AppDomain.CurrentDomain.GetAssemblies()
+ from type in assembly.GetTypes()
+ where type.FullName == classname
+ select type).Count() > 0;
+ }
+
+ private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+
+ public static long GetCurrentUnixTimestampMillis()
+ {
+ return (long)(DateTime.UtcNow - UnixEpoch).TotalMilliseconds;
+ }
+
+ public static long DatetimeToUnixSeconds(DateTime time)
+ {
+ return (long)(time - UnixEpoch).TotalSeconds;
+ }
+
+ public static long GetUnityStartUpTimeStamp()
+ {
+ return GetCurrentUnixTimestampMillis() - (long)EditorApplication.timeSinceStartup * 1000;
+ }
+
+ public static void RegisterEditorUse()
+ {
+ if (s_didTryRegsiterThisSession) return;
+ if (!EditorPrefs.GetBool("thry_has_counted_user", false))
+ {
+ WebHelper.DownloadStringASync(URL.COUNT_USER, delegate (string s)
+ {
+ if (s == "true")
+ EditorPrefs.SetBool("thry_has_counted_user", true);
+ });
+ }
+
+ string projectPrefix = PlayerSettings.companyName + "." + PlayerSettings.productName;
+ if (!EditorPrefs.GetBool(projectPrefix + "_thry_has_counted_project", false))
+ {
+ WebHelper.DownloadStringASync(URL.COUNT_PROJECT, delegate (string s)
+ {
+ if (s == "true")
+ EditorPrefs.SetBool(projectPrefix + "_thry_has_counted_project", true);
+ });
+ }
+ s_didTryRegsiterThisSession = true;
+ }
+
+ //-------------------Comparetors----------------------
+
+ public static int CompareVersions(string v1, string v2)
+ {
+ //fix the string
+ v1 = v1.Replace(",", ".");
+ v2 = v2.Replace(",", ".");
+ Match v1_match = Regex.Match(v1, @"(a|b)?\d+((\.|a|b)\d+)*(a|b)?");
+ Match v2_match = Regex.Match(v2, @"(a|b)?\d+((\.|a|b)\d+)*(a|b)?");
+ if (!v1_match.Success && !v2_match.Success) return 0;
+ else if (!v1_match.Success) return 1;
+ else if (!v2_match.Success) return -1;
+ v1 = v1_match.Value;
+ v2 = v2_match.Value;
+
+ int index_v1 = 0;
+ int index_v2 = 0;
+ string chunk_v1;
+ string chunk_v2;
+ while (index_v1 < v1.Length || index_v2 < v2.Length)
+ {
+ //get a chunk of the strings
+ if (index_v1 < v1.Length)
+ {
+ chunk_v1 = "";
+ if (v1[index_v1] == 'a')
+ chunk_v1 = "-2";
+ else if (v1[index_v1] == 'b')
+ chunk_v1 = "-1";
+ else
+ {
+ while (index_v1 < v1.Length && v1[index_v1] != 'a' && v1[index_v1] != 'b' && v1[index_v1] != '.')
+ chunk_v1 += v1[index_v1++];
+ if (index_v1 < v1.Length && (v1[index_v1] == 'a' || v1[index_v1] == 'b'))
+ index_v1--;
+ }
+ index_v1++;
+ }
+ else
+ chunk_v1 = "0";
+
+ if (index_v2 < v2.Length)
+ {
+ chunk_v2 = "";
+ if (v2[index_v2] == 'a')
+ chunk_v2 = "-2";
+ else if (v2[index_v2] == 'b')
+ chunk_v2 = "-1";
+ else
+ {
+ while (index_v2 < v2.Length && v2[index_v2] != 'a' && v2[index_v2] != 'b' && v2[index_v2] != '.')
+ chunk_v2 += v2[index_v2++];
+ if (index_v2 < v2.Length && (v2[index_v2] == 'a' || v2[index_v2] == 'b'))
+ index_v2--;
+ }
+ index_v2++;
+ }
+ else
+ chunk_v2 = "0";
+
+ //compare chunks
+ int v1P = int.Parse(chunk_v1);
+ int v2P = int.Parse(chunk_v2);
+ if (v1P > v2P) return -1;
+ else if (v1P < v2P) return 1;
+ }
+ return 0;
+ }
+
+ public static bool IsPrimitive(Type t)
+ {
+ return t.IsPrimitive || t == typeof(Decimal) || t == typeof(String);
+ }
+
+ public static string GetStringBetweenBracketsAndAfterId(string input, string id, char[] brackets)
+ {
+ string[] parts = Regex.Split(input, id);
+ if (parts.Length > 1)
+ {
+ char[] behind_id = parts[1].ToCharArray();
+ int i = 0;
+ int begin = 0;
+ int end = behind_id.Length - 1;
+ int depth = 0;
+ bool escaped = false;
+ while (i < behind_id.Length)
+ {
+ if (behind_id[i] == brackets[0] && !escaped)
+ {
+ if (depth == 0)
+ begin = i;
+ depth++;
+ }
+ else if (behind_id[i] == brackets[1] && !escaped)
+ {
+ depth--;
+ if (depth == 0)
+ {
+ end = i;
+ break;
+ }
+ }
+
+ if (behind_id[i] == '\\')
+ escaped = !escaped;
+ else
+ escaped = false;
+ i++;
+ }
+ return parts[1].Substring(begin, end);
+ }
+ return input;
+ }
+
+ public static float SolveMath(string exp, float parameter)
+ {
+ exp = exp.Replace("x", parameter.ToString(CultureInfo.InvariantCulture));
+ exp = exp.Replace(" ", "");
+ float f;
+ if (ExpressionEvaluator.Evaluate<float>(exp, out f)) return f;
+ return 0;
+ }
+ }
+
+ public class PersistentData
+ {
+ public static string Get(string key)
+ {
+ return FileHelper.LoadValueFromFile(key, PATH.PERSISTENT_DATA);
+ }
+
+ public static void Set(string key, string value)
+ {
+ FileHelper.SaveValueToFile(key, value, PATH.PERSISTENT_DATA);
+ }
+
+ public static T Get<T>(string key, T defaultValue)
+ {
+ string s = FileHelper.LoadValueFromFile(key, PATH.PERSISTENT_DATA);
+ if (string.IsNullOrEmpty(s)) return defaultValue;
+ T obj = Parser.Deserialize<T>(s);
+ if (obj == null) return defaultValue;
+ return obj;
+ }
+
+ public static void Set(string key, object value)
+ {
+ FileHelper.SaveValueToFile(key, Parser.Serialize(value), PATH.PERSISTENT_DATA);
+ }
+ }
+
+ public class FileHelper
+ {
+ public static string FindFile(string name, string type=null)
+ {
+ string[] guids;
+ if (type != null)
+ guids = AssetDatabase.FindAssets(name + " t:" + type);
+ else
+ guids = AssetDatabase.FindAssets(name);
+ if (guids.Length == 0)
+ return null;
+ return AssetDatabase.GUIDToAssetPath(guids[0]);
+ }
+
+ //-----------------------Value To File Saver----------------------
+
+ private static Dictionary<string, Dictionary<string,string>> s_textFileData = new Dictionary<string, Dictionary<string, string>>();
+
+ public static string LoadValueFromFile(string key, string path)
+ {
+ if (!s_textFileData.ContainsKey(path)) ReadFileIntoTextFileData(path);
+ if (s_textFileData[path].ContainsKey(key))
+ return s_textFileData[path][key];
+ return null;
+ }
+
+ private static void ReadFileIntoTextFileData(string path)
+ {
+ string data = ReadFileIntoString(path);
+ Dictionary<string, string> dictionary = new Dictionary<string, string>();
+ MatchCollection matchCollection = Regex.Matches(data, @".*\s*:=.*(?=\r?\n)");
+ foreach(Match m in matchCollection)
+ {
+ string[] keyvalue = m.Value.Split(new string[] { ":=" }, 2, StringSplitOptions.RemoveEmptyEntries);
+ if(keyvalue.Length>1)
+ dictionary[keyvalue[0]] = keyvalue[1];
+ }
+ s_textFileData[path] = dictionary;
+ }
+
+ public static bool SaveValueToFile(string key, string value, string path)
+ {
+ if (!s_textFileData.ContainsKey(path)) ReadFileIntoTextFileData(path);
+ s_textFileData[path][key] = value;
+ return SaveDictionaryToFile(path, s_textFileData[path]);
+ }
+
+ public static void RemoveValueFromFile(string key, string path)
+ {
+ if (!s_textFileData.ContainsKey(path)) ReadFileIntoTextFileData(path);
+ if (s_textFileData[path].ContainsKey(key)) s_textFileData[path].Remove(key);
+ }
+
+ private static bool SaveDictionaryToFile(string path, Dictionary<string,string> dictionary)
+ {
+ s_textFileData[path] = dictionary;
+ string data = s_textFileData[path].Aggregate("", (d1, d2) => d1 + d2.Key + ":=" + d2.Value + "\n");
+ WriteStringToFile(data, path);
+ return true;
+ }
+
+ //-----------------------File Interaction---------------------
+
+ public static string FindFileAndReadIntoString(string fileName)
+ {
+ string[] guids = AssetDatabase.FindAssets(fileName);
+ if (guids.Length > 0)
+ return ReadFileIntoString(AssetDatabase.GUIDToAssetPath(guids[0]));
+ else return "";
+ }
+
+ public static void FindFileAndWriteString(string fileName, string s)
+ {
+ string[] guids = AssetDatabase.FindAssets(fileName);
+ if (guids.Length > 0)
+ WriteStringToFile(s, AssetDatabase.GUIDToAssetPath(guids[0]));
+ }
+
+ public static string ReadFileIntoString(string path)
+ {
+ if (!File.Exists(path))
+ {
+ CreateFileWithDirectories(path);
+ return "";
+ }
+ StreamReader reader = new StreamReader(path);
+ string ret = reader.ReadToEnd();
+ reader.Close();
+ return ret;
+ }
+
+ public static void WriteStringToFile(string s, string path)
+ {
+ if (!File.Exists(path)) CreateFileWithDirectories(path);
+ StreamWriter writer = new StreamWriter(path, false);
+ writer.Write(s);
+ writer.Close();
+ }
+
+ public static bool WriteBytesToFile(byte[] bytes, string path)
+ {
+ if (!File.Exists(path)) CreateFileWithDirectories(path);
+ try
+ {
+ using (var fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write))
+ {
+ fs.Write(bytes, 0, bytes.Length);
+ return true;
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Log("Exception caught in process: " + ex.ToString());
+ return false;
+ }
+ }
+
+ public static void CreateFileWithDirectories(string path)
+ {
+ string dir_path = Path.GetDirectoryName(path);
+ if (dir_path != "")
+ Directory.CreateDirectory(dir_path);
+ File.Create(path).Close();
+ }
+ }
+
+ public class TrashHandler
+ {
+ public static void EmptyThryTrash()
+ {
+ if (Directory.Exists(PATH.DELETING_DIR))
+ {
+ DeleteDirectory(PATH.DELETING_DIR);
+ }
+ }
+
+ public static void MoveDirectoryToTrash(string path)
+ {
+ string name = Path.GetFileName(path);
+ if (!Directory.Exists(PATH.DELETING_DIR))
+ Directory.CreateDirectory(PATH.DELETING_DIR);
+ int i = 0;
+ string newpath = PATH.DELETING_DIR + "/" + name + i;
+ while (Directory.Exists(newpath))
+ newpath = PATH.DELETING_DIR + "/" + name + (++i);
+ Directory.Move(path, newpath);
+ }
+
+ static void DeleteDirectory(string path)
+ {
+ foreach (string f in Directory.GetFiles(path))
+ DeleteFile(f);
+ foreach (string d in Directory.GetDirectories(path))
+ DeleteDirectory(d);
+ if (Directory.GetFiles(path).Length + Directory.GetDirectories(path).Length == 0)
+ Directory.Delete(path);
+ }
+ static void DeleteFile(string path)
+ {
+ try
+ {
+ File.Delete(path);
+ }
+ catch (Exception e)
+ {
+ e.GetType();
+ }
+ }
+ }
+
+ public class TextureHelper
+ {
+ public static Gradient GetGradient(Texture texture)
+ {
+ if (texture != null)
+ {
+ string path = AssetDatabase.GetAssetPath(texture);
+ string gradient_data_string = null;
+ if(path != null) gradient_data_string = FileHelper.LoadValueFromFile(AssetDatabase.AssetPathToGUID(path), PATH.GRADIENT_INFO_FILE);
+ //For Backwards compatibility check old id (name) if guid cant be found
+ if(gradient_data_string == null) gradient_data_string = FileHelper.LoadValueFromFile(texture.name, PATH.GRADIENT_INFO_FILE);
+ if (gradient_data_string != null)
+ {
+ Debug.Log(texture.name + " Gradient loaded from file.");
+ Gradient g = Parser.Deserialize<Gradient>(gradient_data_string);
+ return g;
+ }
+ Debug.Log(texture.name + " Converted into Gradient.");
+ return Converter.TextureToGradient(GetReadableTexture(texture));
+ }
+ return new Gradient();
+ }
+
+ private static Texture2D s_BackgroundTexture;
+
+ public static Texture2D GetBackgroundTexture()
+ {
+ if (s_BackgroundTexture == null)
+ s_BackgroundTexture = CreateCheckerTexture(32, 4, 4, Color.white, new Color(0.7f, 0.7f, 0.7f));
+ return s_BackgroundTexture;
+ }
+
+ public static Texture2D CreateCheckerTexture(int numCols, int numRows, int cellPixelWidth, Color col1, Color col2)
+ {
+ int height = numRows * cellPixelWidth;
+ int width = numCols * cellPixelWidth;
+
+ Texture2D texture = new Texture2D(width, height, TextureFormat.RGBA32, false);
+ texture.hideFlags = HideFlags.HideAndDontSave;
+ Color[] pixels = new Color[width * height];
+
+ for (int i = 0; i < numRows; i++)
+ for (int j = 0; j < numCols; j++)
+ for (int ci = 0; ci < cellPixelWidth; ci++)
+ for (int cj = 0; cj < cellPixelWidth; cj++)
+ pixels[(i * cellPixelWidth + ci) * width + j * cellPixelWidth + cj] = ((i + j) % 2 == 0) ? col1 : col2;
+
+ texture.SetPixels(pixels);
+ texture.Apply();
+ return texture;
+ }
+
+ public static Texture SaveTextureAsPNG(Texture2D texture, string path, TextureData settings = null)
+ {
+ if (!path.EndsWith(".png"))
+ path += ".png";
+ byte[] encoding = texture.EncodeToPNG();
+ Debug.Log("Texture saved at \"" + path + "\".");
+ FileHelper.WriteBytesToFile(encoding, path);
+
+ AssetDatabase.ImportAsset(path);
+ if (settings != null)
+ settings.ApplyModes(path);
+ Texture saved = AssetDatabase.LoadAssetAtPath<Texture>(path);
+ return saved;
+ }
+
+ public static void MakeTextureReadible(string path)
+ {
+ TextureImporter importer = (TextureImporter)TextureImporter.GetAtPath(path);
+ if (!importer.isReadable)
+ {
+ importer.isReadable = true;
+ importer.SaveAndReimport();
+ }
+ }
+
+ public static Texture2D GetReadableTexture(Texture texture)
+ {
+ RenderTexture temp = RenderTexture.GetTemporary(texture.width, texture.height, 0, RenderTextureFormat.Default, RenderTextureReadWrite.Linear);
+ Graphics.Blit(texture, temp);
+ RenderTexture previous = RenderTexture.active;
+ RenderTexture.active = temp;
+ Texture2D ret = new Texture2D(texture.width, texture.height);
+ ret.ReadPixels(new Rect(0, 0, temp.width, temp.height), 0, 0);
+ ret.Apply();
+ RenderTexture.active = previous;
+ RenderTexture.ReleaseTemporary(temp);
+ return ret;
+ }
+
+ public static Texture2D Resize(Texture2D texture, int width, int height)
+ {
+ Texture2D ret = new Texture2D(width, height, texture.format, texture.mipmapCount > 0);
+ float scaleX = ((float)texture.width) / width;
+ float scaleY = ((float)texture.height) / height;
+ for (int x = 0; x < width; x++)
+ {
+ for (int y = 0; y < height; y++)
+ {
+ ret.SetPixel(x, y, texture.GetPixel((int)(scaleX * x), (int)(scaleY * y)));
+ }
+ }
+ ret.Apply();
+ return ret;
+ }
+
+ //===============TGA Loader by aaro4130 https://forum.unity.com/threads/tga-loader-for-unity3d.172291/==============
+
+ public static Texture2D LoadTGA(string TGAFile)
+ {
+ using (BinaryReader r = new BinaryReader(File.Open(TGAFile, FileMode.Open)))
+ {
+ byte IDLength = r.ReadByte();
+ byte ColorMapType = r.ReadByte();
+ byte ImageType = r.ReadByte();
+ Int16 CMapStart = r.ReadInt16();
+ Int16 CMapLength = r.ReadInt16();
+ byte CMapDepth = r.ReadByte();
+ Int16 XOffset = r.ReadInt16();
+ Int16 YOffset = r.ReadInt16();
+ Int16 Width = r.ReadInt16();
+ Int16 Height = r.ReadInt16();
+ byte PixelDepth = r.ReadByte();
+ byte ImageDescriptor = r.ReadByte();
+ if (ImageType == 0)
+ {
+ Debug.Log("Unsupported TGA file! No image data");
+ }
+ else if (ImageType == 3 | ImageType == 11)
+ {
+ Debug.Log("Unsupported TGA file! Not truecolor");
+ }
+ else if (ImageType == 9 | ImageType == 10)
+ {
+ Debug.Log("Unsupported TGA file! Colormapped");
+
+ }
+ // MsgBox("Dimensions are " Width "," Height)
+ Texture2D b = new Texture2D(Width, Height, TextureFormat.ARGB32, false);
+ for (int y = 0; y <= b.height - 1; y++)
+ {
+ for (int x = 0; x <= b.width - 1; x++)
+ {
+
+ if (PixelDepth == 32)
+ {
+
+ float red = Convert.ToSingle(r.ReadByte());
+ float green = Convert.ToSingle(r.ReadByte());
+ float blue = Convert.ToSingle(r.ReadByte());
+ float alpha = Convert.ToSingle(r.ReadByte());
+ alpha /= 255;
+ green /= 255;
+ blue /= 255;
+ red /= 255;
+ Color cl = new Color(blue, green, red, alpha);
+ b.SetPixel(x, y, cl);
+
+
+ }
+ else
+ {
+
+ float red = Convert.ToSingle(r.ReadByte());
+ float green = Convert.ToSingle(r.ReadByte());
+ float blue = Convert.ToSingle(r.ReadByte());
+
+
+ green = Mathf.Pow(green / 255, 1 / 2.2f);
+ blue = Mathf.Pow(blue / 255, 1 / 2.2f);
+ red = Mathf.Pow(red / 255, 1 / 2.2f);
+ Color cl = new Color(blue, green, red, 1);
+ b.SetPixel(x, y, cl);
+
+
+ }
+
+ }
+ }
+ b.Apply();
+
+ return b;
+ }
+ }
+ }
+
+ public class MaterialHelper
+ {
+ public static void ToggleKeyword(Material material, string keyword, bool turn_on)
+ {
+ bool is_on = material.IsKeywordEnabled(keyword);
+ if (is_on && !turn_on)
+ material.DisableKeyword(keyword);
+ else if (!is_on && turn_on)
+ material.EnableKeyword(keyword);
+ }
+
+ public static void ToggleKeyword(Material[] materials, string keyword, bool on)
+ {
+ foreach (Material m in materials)
+ ToggleKeyword(m, keyword, on);
+ }
+
+ public static void ToggleKeyword(MaterialProperty p, string keyword, bool on)
+ {
+ ToggleKeyword(p.targets as Material[], keyword, on);
+ }
+
+ /// <summary>
+ /// Set Material Property value or Renderqueue of current Editor.
+ /// </summary>
+ /// <param name="key">Property Name or "render_queue"</param>
+ /// <param name="value"></param>
+ public static void SetMaterialValue(string key, string value)
+ {
+ Material[] materials = ShaderEditor.Active.Materials;
+ MaterialProperty p = ShaderEditor.Active.GetMaterialProperty(key);
+ if (p != null)
+ {
+ MaterialHelper.SetMaterialPropertyValue(p, value);
+ }
+ else if (key == "render_queue")
+ {
+ int q = 0;
+ if (int.TryParse(value, out q))
+ {
+ foreach (Material m in materials) m.renderQueue = q;
+ }
+ }else if (key == "render_type")
+ {
+ foreach (Material m in materials) m.SetOverrideTag("RenderType", value);
+ }
+ }
+
+ public static void SetMaterialPropertyValue(MaterialProperty p, string value)
+ {
+ object prev = null;
+ if (p.type == MaterialProperty.PropType.Texture)
+ {
+ prev = p.textureValue;
+ p.textureValue = AssetDatabase.LoadAssetAtPath<Texture>(value);
+ }
+ else if (p.type == MaterialProperty.PropType.Float || p.type == MaterialProperty.PropType.Range)
+ {
+ float f_value;
+ if (float.TryParse(Parser.GlobalizationFloat(value), out f_value))
+ {
+ prev = p.floatValue;
+ p.floatValue = f_value;
+
+ }
+ }
+ else if (p.type == MaterialProperty.PropType.Vector)
+ {
+ prev = p.vectorValue;
+ p.vectorValue = Converter.StringToVector(value);
+ }
+ else if (p.type == MaterialProperty.PropType.Color)
+ {
+ prev = p.colorValue;
+ p.colorValue = Converter.StringToColor(value);
+ }
+ if (p.applyPropertyCallback != null)
+ p.applyPropertyCallback.Invoke(p, 1, prev);
+ }
+
+ public static void CopyPropertyValueFromMaterial(MaterialProperty p, Material source)
+ {
+ object prev = null;
+ switch (p.type)
+ {
+ case MaterialProperty.PropType.Float:
+ case MaterialProperty.PropType.Range:
+ prev = p.floatValue;
+ p.floatValue = source.GetFloat(p.name);
+ break;
+ case MaterialProperty.PropType.Color:
+ prev = p.colorValue;
+ p.colorValue = source.GetColor(p.name);
+ break;
+ case MaterialProperty.PropType.Vector:
+ prev = p.vectorValue;
+ p.vectorValue = source.GetVector(p.name);
+ break;
+ case MaterialProperty.PropType.Texture:
+ prev = p.textureValue;
+ p.textureValue = source.GetTexture(p.name);
+ Vector2 offset = source.GetTextureOffset(p.name);
+ Vector2 scale = source.GetTextureScale(p.name);
+ p.textureScaleAndOffset = new Vector4(scale.x, scale.y, offset.x, offset.y);
+ break;
+ }
+ if (p.applyPropertyCallback != null)
+ p.applyPropertyCallback.Invoke(p, 1, prev);
+ }
+
+ public static void CopyMaterialValueFromProperty(MaterialProperty target, MaterialProperty source)
+ {
+ object prev = null;
+ switch (target.type)
+ {
+ case MaterialProperty.PropType.Float:
+ case MaterialProperty.PropType.Range:
+ prev = target.floatValue;
+ target.floatValue = source.floatValue;
+ break;
+ case MaterialProperty.PropType.Color:
+ prev = target.colorValue;
+ target.colorValue = source.colorValue;
+ break;
+ case MaterialProperty.PropType.Vector:
+ prev = target.vectorValue;
+ target.vectorValue = source.vectorValue;
+ break;
+ case MaterialProperty.PropType.Texture:
+ prev = target.textureValue;
+ target.textureValue = source.textureValue;
+ target.textureScaleAndOffset = source.textureScaleAndOffset;
+ break;
+ }
+ if (target.applyPropertyCallback != null)
+ target.applyPropertyCallback.Invoke(target, 1, prev);
+ }
+
+ public static void CopyPropertyValueToMaterial(MaterialProperty source, Material target)
+ {
+ CopyMaterialValueFromProperty(MaterialEditor.GetMaterialProperty(new Material[] { target }, source.name), source);
+ }
+ }
+
+ public class ColorHelper
+ {
+ public static Color Subtract(Color col1, Color col2)
+ {
+ return ColorMath(col1, col2, 1, -1);
+ }
+
+ public static Color ColorMath(Color col1, Color col2, float multiplier1, float multiplier2)
+ {
+ return new Color(col1.r * multiplier1 + col2.r * multiplier2, col1.g * multiplier1 + col2.g * multiplier2, col1.b * multiplier1 + col2.b * multiplier2);
+ }
+
+ public static float ColorDifference(Color col1, Color col2)
+ {
+ return Math.Abs(col1.r - col2.r) + Math.Abs(col1.g - col2.g) + Math.Abs(col1.b - col2.b) + Math.Abs(col1.a - col2.a);
+ }
+ }
+
+ public class Converter
+ {
+
+ public static Color StringToColor(string s)
+ {
+ s = s.Trim(new char[] { '(', ')' });
+ string[] split = s.Split(",".ToCharArray());
+ float[] rgba = new float[4] { 1, 1, 1, 1 };
+ for (int i = 0; i < split.Length; i++) if (string.IsNullOrWhiteSpace(split[i]) == false) rgba[i] = float.Parse(split[i]);
+ return new Color(rgba[0], rgba[1], rgba[2], rgba[3]);
+
+ }
+
+ public static Vector4 StringToVector(string s)
+ {
+ s = s.Trim(new char[] { '(', ')' });
+ string[] split = s.Split(",".ToCharArray());
+ float[] xyzw = new float[4];
+ for (int i = 0; i < 4 && i < split.Length; i++) if (string.IsNullOrWhiteSpace(split[i]) == false) xyzw[i] = float.Parse(split[i]); else xyzw[i] = 0;
+ return new Vector4(xyzw[0], xyzw[1], xyzw[2], xyzw[3]);
+ }
+
+ public static string ArrayToString(object[] a)
+ {
+ string ret = "";
+ foreach (object o in a)
+ ret += o.ToString() + ",";
+ return ret.TrimEnd(new char[] { ',' });
+ }
+
+ public static string ArrayToString(Array a)
+ {
+ string ret = "";
+ foreach (object o in a)
+ ret += o.ToString() + ",";
+ return ret.TrimEnd(new char[] { ',' });
+ }
+
+ //--Start--Gradient
+ public static Gradient TextureToGradient(Texture2D texture)
+ {
+ texture = Gradient_Resize(texture);
+ Color[] values = Gradient_Sample(texture);
+ //values = Gradient_Smooth(values);
+ Color[] delta = CalcDelta(values);
+ delta[0] = delta[1];
+ Color[] delta_delta = CalcDelta(delta);
+ //PrintColorArray(delta_delta);
+ List<Color[]> changes = DeltaDeltaToChanges(delta_delta, values);
+ changes = RemoveChangesUnderDistanceThreshold(changes);
+ SortChanges(changes);
+ //PrintColorList(changes);
+ return ConstructGradient(changes, values);
+ }
+
+ private static Texture2D Gradient_Resize(Texture2D texture)
+ {
+ return TextureHelper.Resize(texture, 512, 512);
+ }
+
+ private static Color[] Gradient_Sample(Texture2D texture)
+ {
+ texture.wrapMode = TextureWrapMode.Clamp;
+ int length = texture.width;
+ Color[] ar = new Color[length];
+ for (int i = 0; i < length; i++)
+ {
+ ar[i] = texture.GetPixel(i, i);
+ }
+ return ar;
+ }
+
+ private static Color[] Gradient_Smooth(Color[] values)
+ {
+ Color[] ar = new Color[values.Length];
+ ar[0] = values[0];
+ ar[ar.Length - 1] = values[ar.Length - 1];
+ for (int i = 1; i < values.Length - 1; i++)
+ {
+ ar[i] = new Color();
+ ar[i].r = (values[i - 1].r + values[i].r + values[i + 1].r) / 3;
+ ar[i].g = (values[i - 1].g + values[i].g + values[i + 1].g) / 3;
+ ar[i].b = (values[i - 1].b + values[i].b + values[i + 1].b) / 3;
+ }
+ return ar;
+ }
+
+ private static Color[] CalcDelta(Color[] values)
+ {
+ Color[] delta = new Color[values.Length];
+ delta[0] = new Color(0, 0, 0);
+ for (int i = 1; i < values.Length; i++)
+ {
+ delta[i] = ColorSubtract(values[i - 1], values[i]);
+ }
+ return delta;
+ }
+
+ private static List<Color[]> DeltaDeltaToChanges(Color[] deltadelta, Color[] values)
+ {
+ List<Color[]> changes = new List<Color[]>();
+ for (int i = 0; i < deltadelta.Length; i++)
+ {
+ if (deltadelta[i].r != 0 || deltadelta[i].g != 0 || deltadelta[i].b != 0)
+ {
+ deltadelta[i].a = i / 512.0f;
+ Color[] new_change = new Color[2];
+ new_change[0] = deltadelta[i];
+ new_change[1] = values[i];
+ changes.Add(new_change);
+ }
+ }
+ return changes;
+ }
+
+ const float GRADIENT_DISTANCE_THRESHOLD = 0.05f;
+ private static List<Color[]> RemoveChangesUnderDistanceThreshold(List<Color[]> changes)
+ {
+ List<Color[]> new_changes = new List<Color[]>();
+ new_changes.Add(changes[0]);
+ for (int i = 1; i < changes.Count; i++)
+ {
+
+ if (changes[i][0].a - new_changes[new_changes.Count - 1][0].a < GRADIENT_DISTANCE_THRESHOLD)
+ {
+ if (ColorValueForDelta(new_changes[new_changes.Count - 1][0]) < ColorValueForDelta(changes[i][0]))
+ {
+ new_changes.RemoveAt(new_changes.Count - 1);
+ new_changes.Add(changes[i]);
+ }
+ }
+ else
+ {
+ new_changes.Add(changes[i]);
+ }
+ }
+ return new_changes;
+ }
+
+ private static void SortChanges(List<Color[]> changes)
+ {
+ changes.Sort(delegate (Color[] x, Color[] y)
+ {
+ float sizeX = ColorValueForDelta(x[0]);
+ float sizeY = ColorValueForDelta(y[0]);
+ if (sizeX < sizeY) return 1;
+ else if (sizeY < sizeX) return -1;
+ return 0;
+ });
+ }
+
+ private static Gradient ConstructGradient(List<Color[]> changes, Color[] values)
+ {
+ List<GradientAlphaKey> alphas = new List<GradientAlphaKey>();
+ List<GradientColorKey> colors = new List<GradientColorKey>();
+ for (int i = 0; i < 6 && i < changes.Count; i++)
+ {
+ colors.Add(new GradientColorKey(changes[i][1], changes[i][0].a));
+ //Debug.Log("key " + changes[i][0].a);
+ }
+ colors.Add(new GradientColorKey(values[0], 0));
+ colors.Add(new GradientColorKey(values[values.Length - 1], 1));
+ alphas.Add(new GradientAlphaKey(1, 0));
+ alphas.Add(new GradientAlphaKey(1, 1));
+ Gradient gradient = new Gradient();
+ gradient.SetKeys(colors.ToArray(), alphas.ToArray());
+ return gradient;
+ }
+
+ private static void PrintColorArray(Color[] ar)
+ {
+ foreach (Color c in ar)
+ Debug.Log(c.ToString());
+ }
+ private static void PrintColorList(List<Color[]> ar)
+ {
+ foreach (Color[] x in ar)
+ Debug.Log(ColorValueForDelta(x[0]) + ":" + x[0].ToString());
+ }
+
+ private static float ColorValueForDelta(Color col)
+ {
+ return Mathf.Abs(col.r) + Mathf.Abs(col.g) + Mathf.Abs(col.b);
+ }
+
+ private static Color ColorAdd(Color col1, Color col2)
+ {
+ return new Color(col1.r + col2.r, col1.g + col2.g, col1.b + col2.b);
+ }
+ private static Color ColorSubtract(Color col1, Color col2)
+ {
+ return new Color(col1.r - col2.r, col1.g - col2.g, col1.b - col2.b);
+ }
+
+ public static Texture2D GradientToTexture(Gradient gradient, int width, int height)
+ {
+ width = Mathf.Max(0, Mathf.Min(8192, width));
+ height = Mathf.Max(0, Mathf.Min(8192, height));
+ Texture2D texture = new Texture2D(width, height);
+ for (int x = 0; x < width; x++)
+ {
+ Color col = gradient.Evaluate((float)x / width);
+ for (int y = 0; y < height; y++) texture.SetPixel(x, y, col);
+ }
+ texture.Apply();
+ return texture;
+ }
+
+ //--End--Gradient
+
+ public static Texture2D CurveToTexture(AnimationCurve curve, TextureData texture_settings)
+ {
+ Texture2D texture = new Texture2D(texture_settings.width, texture_settings.height);
+ for (int i = 0; i < texture_settings.width; i++)
+ {
+ Color color = new Color();
+ float value = curve.Evaluate((float)i / texture_settings.width);
+ value = Mathf.Clamp01(value);
+ if (texture_settings.channel == 'r')
+ color.r = value;
+ else if (texture_settings.channel == 'g')
+ color.g = value;
+ else if (texture_settings.channel == 'b')
+ color.b = value;
+ else if (texture_settings.channel == 'a')
+ color.a = value;
+ if (texture_settings.channel != 'a')
+ color.a = 1;
+ for (int y = 0; y < texture_settings.height; y++)
+ texture.SetPixel(i, y, color);
+ }
+ texture.Apply();
+ texture_settings.ApplyModes(texture);
+ return texture;
+ }
+
+ //==============Texture Array=================
+
+ [MenuItem("Assets/Thry/Flipbooks/Images 2 TextureArray",false, 303)]
+ static void SelectionImagesToTextureArray()
+ {
+ string[] paths = Selection.assetGUIDs.Select(g => AssetDatabase.GUIDToAssetPath(g)).ToArray();
+ PathsToTexture2DArray(paths);
+ }
+
+ [MenuItem("Assets/Thry/Flipbooks/Images 2 TextureArray", true)]
+ static bool SelectionImagesToTextureArrayValidator()
+ {
+ if (Selection.assetGUIDs != null && Selection.assetGUIDs.Length > 0)
+ {
+ return Selection.assetGUIDs.All(g => Regex.IsMatch(AssetDatabase.GUIDToAssetPath(g), @".*\.(png)|(jpg)"));
+ }
+ return false;
+ }
+
+ public static Texture2DArray PathsToTexture2DArray(string[] paths)
+ {
+ if (paths.Length == 0)
+ return null;
+ if (paths[0].EndsWith(".gif"))
+ {
+ return Converter.GifToTextureArray(paths[0]);
+ }
+ else
+ {
+#if SYSTEM_DRAWING
+ Texture2D[] wew = paths.Where(p=> AssetDatabase.GetMainAssetTypeAtPath(p).IsAssignableFrom(typeof(Texture2D))).Select(p => AssetDatabase.LoadAssetAtPath<Texture2D>(p)).ToArray();
+ Array.Sort(wew, (UnityEngine.Object one, UnityEngine.Object two) => one.name.CompareTo(two.name));
+ Selection.objects = wew;
+ Texture2DArray texture2DArray = new Texture2DArray(wew[0].width, wew[0].height, wew.Length, wew[0].format, true);
+
+ string assetPath = AssetDatabase.GetAssetPath(wew[0]);
+ assetPath = assetPath.Remove(assetPath.LastIndexOf('/')) + "/Texture2DArray.asset";
+
+ for (int i = 0; i < wew.Length; i++)
+ {
+ for (int m = 0; m < wew[i].mipmapCount; m++)
+ {
+ Graphics.CopyTexture(wew[i], 0, m, texture2DArray, i, m);
+ }
+ }
+
+ texture2DArray.anisoLevel = wew[0].anisoLevel;
+ texture2DArray.wrapModeU = wew[0].wrapModeU;
+ texture2DArray.wrapModeV = wew[0].wrapModeV;
+ texture2DArray.Apply(false, true);
+
+ AssetDatabase.CreateAsset(texture2DArray, assetPath);
+ AssetDatabase.SaveAssets();
+
+ Selection.activeObject = texture2DArray;
+ return texture2DArray;
+#else
+ return null;
+#endif
+ }
+ }
+
+ [MenuItem("Assets/Thry/Flipbooks/Gif 2 TextureArray",false, 303)]
+ static void SelectionGifToTextureArray()
+ {
+ GifToTextureArray(AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[0]));
+ }
+
+ [MenuItem("Assets/Thry/Flipbooks/Gif 2 TextureArray", true)]
+ static bool SelectionGifToTextureArrayValidator()
+ {
+ if (Selection.assetGUIDs != null && Selection.assetGUIDs.Length > 0)
+ {
+ return AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[0]).EndsWith(".gif");
+ }
+ return false;
+ }
+
+ public static Texture2DArray GifToTextureArray(string path)
+ {
+ List<Texture2D> array = GetGifFrames(path);
+ if (array == null) return null;
+ if (array.Count == 0)
+ {
+ Debug.LogError("Gif is empty or System.Drawing is not working. Try right clicking and reimporting the \"Thry Editor\" Folder!");
+ return null;
+ }
+ Texture2DArray arrayTexture = Textre2DArrayToAsset(array.ToArray());
+ AssetDatabase.CreateAsset(arrayTexture, path.Replace(".gif", ".asset"));
+ AssetDatabase.SaveAssets();
+ return arrayTexture;
+ }
+
+ public static List<Texture2D> GetGifFrames(string path)
+ {
+ List<Texture2D> gifFrames = new List<Texture2D>();
+#if SYSTEM_DRAWING
+ var gifImage = System.Drawing.Image.FromFile(path);
+ var dimension = new System.Drawing.Imaging.FrameDimension(gifImage.FrameDimensionsList[0]);
+
+ int width = Mathf.ClosestPowerOfTwo(gifImage.Width - 1);
+ int height = Mathf.ClosestPowerOfTwo(gifImage.Height - 1);
+
+ bool hasAlpha = false;
+
+ int frameCount = gifImage.GetFrameCount(dimension);
+
+ float totalProgress = frameCount * width;
+ for (int i = 0; i < frameCount; i++)
+ {
+ gifImage.SelectActiveFrame(dimension, i);
+ var ogframe = new System.Drawing.Bitmap(gifImage.Width, gifImage.Height);
+ System.Drawing.Graphics.FromImage(ogframe).DrawImage(gifImage, System.Drawing.Point.Empty);
+ var frame = ResizeBitmap(ogframe, width, height);
+
+ Texture2D frameTexture = new Texture2D(frame.Width, frame.Height);
+
+ float doneProgress = i * width;
+ for (int x = 0; x < frame.Width; x++)
+ {
+ if (x % 20 == 0)
+ if (EditorUtility.DisplayCancelableProgressBar("From GIF", "Frame " + i + ": " + (int)((float)x / width * 100) + "%", (doneProgress + x + 1) / totalProgress))
+ {
+ EditorUtility.ClearProgressBar();
+ return null;
+ }
+
+ for (int y = 0; y < frame.Height; y++)
+ {
+ System.Drawing.Color sourceColor = frame.GetPixel(x, y);
+ frameTexture.SetPixel(x, frame.Height - 1 - y, new UnityEngine.Color32(sourceColor.R, sourceColor.G, sourceColor.B, sourceColor.A));
+ if (sourceColor.A < 255.0f)
+ {
+ hasAlpha = true;
+ }
+ }
+ }
+
+ frameTexture.Apply();
+ gifFrames.Add(frameTexture);
+ }
+ EditorUtility.ClearProgressBar();
+ //Debug.Log("has alpha? " + hasAlpha);
+ for (int i = 0; i < frameCount; i++)
+ {
+ EditorUtility.CompressTexture(gifFrames[i], hasAlpha ? TextureFormat.DXT5 : TextureFormat.DXT1, UnityEditor.TextureCompressionQuality.Normal);
+ gifFrames[i].Apply(true, false);
+ }
+#endif
+ return gifFrames;
+ }
+
+#if SYSTEM_DRAWING
+ public static System.Drawing.Bitmap ResizeBitmap(System.Drawing.Image image, int width, int height)
+ {
+ var destRect = new System.Drawing.Rectangle(0, 0, width, height);
+ var destImage = new System.Drawing.Bitmap(width, height);
+
+ destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
+
+ using (var graphics = System.Drawing.Graphics.FromImage(destImage))
+ {
+ graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
+ graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
+ graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
+ graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
+ graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
+
+ using (var wrapMode = new System.Drawing.Imaging.ImageAttributes())
+ {
+ wrapMode.SetWrapMode(System.Drawing.Drawing2D.WrapMode.TileFlipXY);
+ graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, System.Drawing.GraphicsUnit.Pixel, wrapMode);
+ }
+ }
+
+ return destImage;
+ }
+#endif
+
+ private static Texture2DArray Textre2DArrayToAsset(Texture2D[] array)
+ {
+ Texture2DArray texture2DArray = new Texture2DArray(array[0].width, array[0].height, array.Length, array[0].format, true);
+
+#if SYSTEM_DRAWING
+ for (int i = 0; i < array.Length; i++)
+ {
+ for (int m = 0; m < array[i].mipmapCount; m++)
+ {
+ UnityEngine.Graphics.CopyTexture(array[i], 0, m, texture2DArray, i, m);
+ }
+ }
+#endif
+
+ texture2DArray.anisoLevel = array[0].anisoLevel;
+ texture2DArray.wrapModeU = array[0].wrapModeU;
+ texture2DArray.wrapModeV = array[0].wrapModeV;
+
+ texture2DArray.Apply(false, true);
+
+ return texture2DArray;
+ }
+ }
+
+ public class ShaderHelper
+ {
+
+ private static Dictionary<Shader, Dictionary<string, string[]>> shader_property_drawers = new Dictionary<Shader, Dictionary<string, string[]>>();
+ public static string[] GetDrawer(MaterialProperty property)
+ {
+ Shader shader = ((Material)property.targets[0]).shader;
+
+ if (!shader_property_drawers.ContainsKey(shader))
+ LoadShaderPropertyDrawers(shader);
+
+ Dictionary<string, string[]> property_drawers = shader_property_drawers[shader];
+ if (property_drawers.ContainsKey(property.name))
+ return property_drawers[property.name];
+ return null;
+ }
+
+ public static void LoadShaderPropertyDrawers(Shader shader)
+ {
+ string path = AssetDatabase.GetAssetPath(shader);
+ string code = FileHelper.ReadFileIntoString(path);
+ code = Helper.GetStringBetweenBracketsAndAfterId(code, "Properties", new char[] { '{', '}' });
+ MatchCollection matchCollection = Regex.Matches(code, @"\[.*\].*(?=\()");
+ Dictionary<string, string[]> property_drawers = new Dictionary<string, string[]>();
+ foreach (Match match in matchCollection)
+ {
+ string[] drawers_or_flag_code = GetDrawersFlagsCode(match.Value);
+ string drawer_code = GetNonFlagDrawer(drawers_or_flag_code);
+ if (drawer_code == null)
+ continue;
+
+ string property_name = Regex.Match(match.Value, @"(?<=\])[^\[]*$").Value.Trim();
+
+ List<string> drawer_and_parameters = new List<string>();
+ drawer_and_parameters.Add(Regex.Split(drawer_code, @"\(")[0]);
+
+ GetDrawerParameters(drawer_code, drawer_and_parameters);
+
+ property_drawers[property_name] = drawer_and_parameters.ToArray();
+ }
+ shader_property_drawers[shader] = property_drawers;
+ }
+
+ private static void GetDrawerParameters(string code, List<string> list)
+ {
+ MatchCollection matchCollection = Regex.Matches(code, @"(?<=\(|,).*?(?=\)|,)");
+ foreach (Match m in matchCollection)
+ list.Add(m.Value);
+ }
+
+ private static string GetNonFlagDrawer(string[] codes)
+ {
+ foreach (string c in codes)
+ if (!DrawerIsFlag(c))
+ return c;
+ return null;
+ }
+
+ private static bool DrawerIsFlag(string code)
+ {
+ return (code == "HideInInspector" || code == "NoScaleOffset" || code == "Normal" || code == "HDR" || code == "Gamma" || code == "PerRendererData");
+ }
+
+ private static string[] GetDrawersFlagsCode(string line)
+ {
+ MatchCollection matchCollection = Regex.Matches(line, @"(?<=\[).*?(?=\])");
+ string[] codes = new string[matchCollection.Count];
+ int i = 0;
+ foreach (Match m in matchCollection)
+ codes[i++] = m.Value;
+ return codes;
+ }
+ //------------Track ShaderEditor shaders-------------------
+
+ public class ShaderEditorShader
+ {
+ public string path;
+ public string name;
+ public string version;
+ public bool isUsingEditor;
+ }
+
+ private static List<ShaderEditorShader> shaders;
+ private static Dictionary<string, ShaderEditorShader> dictionary;
+ public static List<ShaderEditorShader> thry_editor_shaders
+ {
+ get
+ {
+ Init();
+ return shaders;
+ }
+ }
+
+ private static void Init()
+ {
+ if (shaders == null)
+ LoadShaderEditorShaders();
+ }
+
+ private static void Add(ShaderEditorShader s)
+ {
+ Init();
+ if (dictionary == null || s == null) return;
+ if (!dictionary.ContainsKey(s.name))
+ {
+ dictionary.Add(s.name, s);
+ shaders.Add(s);
+ }
+ }
+
+ private static void RemoveAt(int i)
+ {
+ Init();
+ if (dictionary == null || i >= shaders.Count() || shaders[i] == null) return;
+ if (dictionary.ContainsKey(shaders[i].name))
+ {
+ dictionary.Remove(shaders[i].name);
+ shaders.RemoveAt(i--);
+ }
+ }
+
+ public static string[] GetShaderEditorShaderNames()
+ {
+ string[] r = new string[thry_editor_shaders.Count];
+ for (int i = 0; i < r.Length; i++)
+ r[i] = thry_editor_shaders[i].name;
+ return r;
+ }
+
+ public static bool IsShaderUsingShaderEditor(Shader shader)
+ {
+ Init();
+ return dictionary.ContainsKey(shader.name);
+ }
+
+
+ private static void LoadShaderEditorShaders()
+ {
+ string data = FileHelper.ReadFileIntoString(PATH.THRY_EDITOR_SHADERS);
+ if (data != "")
+ {
+ shaders = Parser.ParseToObject<List<ShaderEditorShader>>(data);
+ InitDictionary();
+ }
+ else
+ {
+ dictionary = new Dictionary<string, ShaderEditorShader>();
+ SearchAllShadersForShaderEditorUsage();
+ }
+ DeleteNull();
+ }
+
+ private static void InitDictionary()
+ {
+ dictionary = new Dictionary<string, ShaderEditorShader>();
+ foreach (ShaderEditorShader s in shaders)
+ {
+ if (s != null && s.name != null && dictionary.ContainsKey(s.name) == false)
+ dictionary.Add(s.name, s);
+ }
+ }
+
+ public static void SearchAllShadersForShaderEditorUsage()
+ {
+ shaders = new List<ShaderEditorShader>();
+ string[] guids = AssetDatabase.FindAssets("t:shader");
+ foreach (string g in guids)
+ {
+ string path = AssetDatabase.GUIDToAssetPath(g);
+ TestShaderForShaderEditor(path);
+ }
+ Save();
+ }
+
+ private static void DeleteNull()
+ {
+ bool save = false;
+ int length = shaders.Count;
+ for (int i = 0; i < length; i++)
+ {
+ if (shaders[i] == null)
+ {
+ RemoveAt(i--);
+ length--;
+ save = true;
+ }
+ }
+ if (save)
+ Save();
+ }
+
+ private static void Save()
+ {
+ FileHelper.WriteStringToFile(Parser.ObjectToString(shaders), PATH.THRY_EDITOR_SHADERS);
+ }
+
+ private static string GetActiveCustomEditorParagraph(string code)
+ {
+ Match match = Regex.Match(code, @"(^|\*\/)((.|\n)(?!(\/\*)))*CustomEditor\s*\""(\w|\d)*\""((.|\n)(?!(\/\*)))*");
+ if (match.Success) return match.Value;
+ return null;
+ }
+
+ private static bool ParagraphContainsActiveShaderEditorDefinition(string code)
+ {
+ Match match = Regex.Match(code, @"\n\s+CustomEditor\s+\""ShaderEditor\""");
+ return match.Success;
+ }
+
+ private static bool ShaderUsesShaderEditor(string code)
+ {
+ string activeCustomEditorParagraph = GetActiveCustomEditorParagraph(code);
+ if (activeCustomEditorParagraph == null)
+ return false;
+ return ParagraphContainsActiveShaderEditorDefinition(activeCustomEditorParagraph);
+ }
+
+ private static bool TestShaderForShaderEditor(string path)
+ {
+ string code = FileHelper.ReadFileIntoString(path);
+ if (ShaderUsesShaderEditor(code))
+ {
+ ShaderEditorShader shader = new ShaderEditorShader();
+ shader.path = path;
+ Match name_match = Regex.Match(code, @"(?<=[Ss]hader)\s*\""[^\""]+(?=\""\s*{)");
+ if (name_match.Success) shader.name = name_match.Value.TrimStart(new char[] { ' ', '"' });
+ Match master_label_match = Regex.Match(code, @"\[HideInInspector\]\s*shader_master_label\s*\(\s*\""[^\""]*(?=\"")");
+ if (master_label_match.Success) shader.version = GetVersionFromMasterLabel(master_label_match.Value);
+ Add(shader);
+ return true;
+ }
+ return false;
+ }
+
+ private static string GetVersionFromMasterLabel(string label)
+ {
+ Match match = Regex.Match(label, @"(?<=v|V)\d+(\.\d+)*");
+ if (!match.Success)
+ match = Regex.Match(label, @"\d+(\.\d+)+");
+ if (match.Success)
+ return match.Value;
+ return null;
+ }
+
+ public static void AssetsImported(string[] paths)
+ {
+ bool save = false;
+ foreach (string path in paths)
+ {
+ if (!path.EndsWith(".shader"))
+ continue;
+ if (TestShaderForShaderEditor(path))
+ save = true;
+ }
+ if (save)
+ Save();
+ }
+
+ public static void AssetsDeleted(string[] paths)
+ {
+ bool save = false;
+ foreach (string path in paths)
+ {
+ if (!path.EndsWith(".shader"))
+ continue;
+ int length = thry_editor_shaders.Count;
+ for (int i = 0; i < length; i++)
+ {
+ if (thry_editor_shaders[i] != null && thry_editor_shaders[i].path == path)
+ {
+ RemoveAt(i--);
+ length--;
+ save = true;
+ }
+ }
+ }
+ if (save)
+ Save();
+ }
+
+ public static void AssetsMoved(string[] old_paths, string[] paths)
+ {
+ bool save = false;
+ for (int i = 0; i < paths.Length; i++)
+ {
+ if (!paths[i].EndsWith(".shader"))
+ continue;
+ foreach (ShaderEditorShader s in thry_editor_shaders)
+ {
+ if (s == null) continue;
+ if (s.path == old_paths[i])
+ {
+ s.path = paths[i];
+ save = true;
+ }
+ }
+ }
+ if (save)
+ Save();
+ }
+
+ static Dictionary<Shader, bool> usingThryShaderEditor = new Dictionary<Shader, bool>();
+ public static bool IsShaderUsingThryShaderEditor(Shader shader)
+ {
+ if (usingThryShaderEditor.ContainsKey(shader)) return usingThryShaderEditor[shader];
+ usingThryShaderEditor[shader] = Enumerable.Range(0, shader.GetPropertyCount()).Any(i => shader.GetPropertyName(i) == ShaderEditor.PROPERTY_NAME_EDITOR_DETECT);
+ return usingThryShaderEditor[shader];
+ }
+
+ static MethodInfo getPropertyHandlerMethod;
+ static PropertyInfo drawerProperty;
+ static FieldInfo keyWordFieldUnityDefault;
+ static FieldInfo keyWordFieldThry;
+ static bool areKeywordDrawerMethodsInit = false;
+ private static void InitKeywordDrawerMethods()
+ {
+ if (areKeywordDrawerMethodsInit) return;
+ Type materialPropertyDrawerType = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.MaterialPropertyHandler");
+ getPropertyHandlerMethod = materialPropertyDrawerType.GetMethod("GetShaderPropertyHandler", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
+ drawerProperty = materialPropertyDrawerType.GetProperty("propertyDrawer");
+ Type materialToggleDrawerType = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.MaterialToggleDrawer");
+ keyWordFieldUnityDefault = materialToggleDrawerType.GetField("keyword", BindingFlags.Instance | BindingFlags.NonPublic);
+ keyWordFieldThry = typeof(ThryToggleDrawer).GetField("keyword");
+ areKeywordDrawerMethodsInit = true;
+ }
+
+ public static void EnableDisableKeywordsBasedOnTheirFloatValue(IEnumerable<Material> targets, Shader shader, string propertyName)
+ {
+ InitKeywordDrawerMethods();
+ //Handle keywords
+ object propertyHandler = getPropertyHandlerMethod.Invoke(null, new object[] { shader, propertyName });
+ //if has custom drawer
+ if (propertyHandler != null)
+ {
+ object propertyDrawer = drawerProperty.GetValue(propertyHandler, null);
+ //if custom drawer exists
+ if (propertyDrawer != null)
+ {
+ // if is keyword drawer make sure all materials have the keyworkd enabled / disabled depending on their value
+ string keyword = null;
+ if (propertyDrawer.GetType() == typeof(ThryToggleDrawer)){
+ keyword = (string)keyWordFieldThry.GetValue(propertyDrawer);
+ }else if (propertyDrawer.GetType().ToString() == "UnityEditor.MaterialToggleDrawer")
+ {
+ keyword = (string)keyWordFieldUnityDefault.GetValue(propertyDrawer);
+ } if(keyword != null) {
+ foreach (Material m in targets)
+ {
+ if (m.GetFloat(propertyName) == 1)
+ m.EnableKeyword(keyword);
+ else
+ m.DisableKeyword(keyword);
+ }
+ }
+ }
+ }
+ }
+
+ }
+
+ public class StringHelper
+ {
+ public static string GetBetween(string value, string prefix, string postfix)
+ {
+ return GetBetween(value, prefix, postfix, value);
+ }
+
+ public static string GetBetween(string value, string prefix, string postfix, string fallback)
+ {
+ string pattern = @"(?<=" + prefix + ").*?(?=" + postfix + ")";
+ Match m = Regex.Match(value, pattern);
+ if (m.Success)
+ return m.Value;
+ return fallback;
+ }
+
+ //returns data for name:{data} even if data containss brakets
+ public static string GetBracket(string data, string bracketName)
+ {
+ Match m = Regex.Match(data, bracketName + ":");
+ if (m.Success)
+ {
+ int startIndex = m.Index + bracketName.Length + 2;
+ int i = startIndex;
+ int depth = 0;
+ while (++i < data.Length)
+ {
+ if (data[i] == '{')
+ depth++;
+ else if (data[i] == '}')
+ {
+ if (depth == 0)
+ break;
+ depth--;
+ }
+ }
+ return data.Substring(startIndex, i - startIndex);
+ }
+ return data;
+ }
+ }
+
+ public class VRCInterface
+ {
+ private static VRCInterface _Instance;
+ public static VRCInterface Get()
+ {
+ if (_Instance == null) _Instance = new VRCInterface();
+ return _Instance;
+ }
+ public static void Update()
+ {
+ _Instance = new VRCInterface();
+ }
+
+ public SDK_Information Sdk_information;
+
+ public class SDK_Information
+ {
+ public VRC_SDK_Type type;
+ public string installed_version = "0";
+ }
+
+ public enum VRC_SDK_Type
+ {
+ NONE = 0,
+ SDK_2 = 1,
+ SDK_3_Avatar = 2,
+ SDK_3_World = 3
+ }
+
+ private VRCInterface()
+ {
+ Sdk_information = new SDK_Information();
+ Sdk_information.type = GetInstalledSDKType();
+ InitInstalledSDKVersionAndPaths();
+ }
+
+ private void InitInstalledSDKVersionAndPaths()
+ {
+ string[] guids = AssetDatabase.FindAssets("version");
+ string path = null;
+ foreach (string guid in guids)
+ {
+ string p = AssetDatabase.GUIDToAssetPath(guid);
+ if (p.Contains("VRCSDK/version"))
+ path = p;
+ }
+ if (path == null || !File.Exists(path))
+ return;
+ string persistent = PersistentData.Get("vrc_sdk_version");
+ if (persistent != null)
+ Sdk_information.installed_version = persistent;
+ else
+ Sdk_information.installed_version = Regex.Replace(FileHelper.ReadFileIntoString(path), @"\n?\r", "");
+ }
+
+ public static VRC_SDK_Type GetInstalledSDKType()
+ {
+#if VRC_SDK_VRCSDK3 && UDON
+ return VRC_SDK_Type.SDK_3_World;
+#elif VRC_SDK_VRCSDK3
+ return VRC_SDK_Type.SDK_3_Avatar;
+#elif VRC_SDK_VRCSDK2
+ return VRC_SDK_Type.SDK_2;
+#else
+ return VRC_SDK_Type.NONE;
+#endif
+ }
+
+ public static bool IsVRCSDKInstalled()
+ {
+#if VRC_SDK_VRCSDK3
+ return true;
+#elif VRC_SDK_VRCSDK2
+ return true;
+#else
+ return false;
+#endif
+ }
+ }
+}
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Helper.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Helper.cs.meta
new file mode 100644
index 00000000..7c0c79b5
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Helper.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 878844302fe9a8b498f7002ac13ffc7a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/HelperWeb.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/HelperWeb.cs
new file mode 100644
index 00000000..d1a9aed2
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/HelperWeb.cs
@@ -0,0 +1,227 @@
+// Material/Shader Inspector for Unity 2017/2018
+// Copyright (C) 2019 Thryrallo
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Net;
+using System.Net.Security;
+using System.Security.Cryptography.X509Certificates;
+using UnityEditor;
+using UnityEngine;
+using UnityEngine.Networking;
+
+namespace Thry
+{
+ public class WebHelper
+ {
+ public static string FixUrl(string url)
+ {
+ if (!url.StartsWith("http"))
+ url = "http://" + url;
+ url = url.Replace("\\","/");
+ if (System.Text.RegularExpressions.Regex.IsMatch(url, @"^https?:\/[^\/].*"))
+ url = url.Replace(":/", "://");
+ return url;
+ }
+
+ public static string GetFinalRedirect(string url)
+ {
+ if (string.IsNullOrEmpty(url))
+ return url;
+ try
+ {
+ UnityWebRequest request = new UnityWebRequest(url);
+ request.method = UnityWebRequest.kHttpVerbHEAD;
+ DownloadHandlerBuffer response = new DownloadHandlerBuffer();
+ request.downloadHandler = response;
+ request.SendWebRequest();
+ bool fetching = true;
+ while (fetching)
+ {
+ if (request.isHttpError || request.isNetworkError)
+ {
+ fetching = false;
+ Debug.Log(request.error);
+ }
+ if (request.isDone)
+ {
+ fetching = false;
+ }
+ }
+ return request.url;
+ }
+ catch (Exception ex)
+ {
+ ex.ToString();
+ return null;
+ }
+ }
+
+ private static Dictionary<string, string> fileCache = new Dictionary<string, string>();
+ public static string GetCachedString(string url)
+ {
+ if (fileCache.ContainsKey(url) == false) fileCache[url] = DownloadString(url);
+ return fileCache[url];
+ }
+
+ //-------------------Downloaders-----------------------------
+
+ [InitializeOnLoad]
+ public class MainThreader
+ {
+ private struct CallData
+ {
+ public Action<string> action;
+ public object[] arguments;
+ }
+ static List<CallData> queue;
+
+ static MainThreader()
+ {
+ queue = new List<CallData>();
+ EditorApplication.update += Update;
+ }
+
+ public static void Call(Action<string> action, params object[] args)
+ {
+ if (action == null)
+ return;
+ CallData data = new CallData();
+ data.action = action;
+ data.arguments = args;
+ if (args == null || args.Length == 0 || args[0] == null)
+ data.arguments = new object[] { "" };
+ else
+ data.arguments = args;
+ queue.Add(data);
+ }
+
+ public static void Update()
+ {
+ if (queue.Count > 0)
+ {
+ try
+ {
+ queue[0].action.DynamicInvoke(queue[0].arguments);
+ }
+ catch(Exception e) {
+ Debug.LogWarning("[Thry] Error during WebRequest: " + e.ToString());
+ }
+ queue.RemoveAt(0);
+ }
+ }
+ }
+
+ public static void DownloadFile(string url, string path)
+ {
+ DownloadAsFile(url, path);
+ }
+
+ public static void DownloadFileASync(string url, string path, Action<string> callback)
+ {
+ DownloadAsBytesASync(url, delegate (object o, DownloadDataCompletedEventArgs a)
+ {
+ if (a.Cancelled || a.Error != null)
+ MainThreader.Call(callback, null);
+ else
+ {
+ FileHelper.WriteBytesToFile(a.Result, path);
+ MainThreader.Call(callback, path);
+ }
+ });
+ }
+
+ public static string DownloadString(string url)
+ {
+ return DownloadAsString(url);
+ }
+
+ public static void DownloadStringASync(string url, Action<string> callback)
+ {
+ DownloadAsStringASync(url, delegate (object o, DownloadStringCompletedEventArgs e)
+ {
+ if (e.Cancelled || e.Error != null)
+ {
+ Debug.LogWarning(e.Error);
+ MainThreader.Call(callback, null);
+ }
+ else
+ MainThreader.Call(callback, e.Result);
+ });
+ }
+
+ private static void SetCertificate()
+ {
+ ServicePointManager.ServerCertificateValidationCallback =
+ delegate (object s, X509Certificate certificate,
+ X509Chain chain, SslPolicyErrors sslPolicyErrors)
+ { return true; };
+ }
+
+ private static string DownloadAsString(string url)
+ {
+ SetCertificate();
+ string contents = null;
+ try
+ {
+ using (var wc = new System.Net.WebClient())
+ contents = wc.DownloadString(url);
+ }catch(WebException e)
+ {
+ Debug.LogError(e);
+ }
+ return contents;
+ }
+
+ private static void DownloadAsStringASync(string url, Action<object, DownloadStringCompletedEventArgs> callback)
+ {
+ SetCertificate();
+ using (var wc = new System.Net.WebClient())
+ {
+ wc.Headers["User-Agent"] = "Mozilla/4.0 (Compatible; Windows NT 5.1; MSIE 6.0)";
+ wc.DownloadStringCompleted += new DownloadStringCompletedEventHandler(callback);
+ wc.DownloadStringAsync(new Uri(url));
+ }
+ }
+
+ private static void DownloadAsFileASync(string url, string path, Action<object, AsyncCompletedEventArgs> callback)
+ {
+ SetCertificate();
+ using (var wc = new System.Net.WebClient())
+ {
+ wc.DownloadFileCompleted += new AsyncCompletedEventHandler(callback);
+ wc.DownloadFileAsync(new Uri(url), path);
+ }
+ }
+
+ private static void DownloadAsFile(string url, string path)
+ {
+ SetCertificate();
+ using (var wc = new System.Net.WebClient())
+ wc.DownloadFile(url, path);
+ }
+
+ private static byte[] DownloadAsBytes(string url)
+ {
+ SetCertificate();
+ byte[] contents = null;
+ using (var wc = new System.Net.WebClient())
+ contents = wc.DownloadData(url);
+ return contents;
+ }
+
+ private static void DownloadAsBytesASync(string url, Action<object, DownloadDataCompletedEventArgs> callback)
+ {
+ SetCertificate();
+ using (var wc = new System.Net.WebClient())
+ {
+ wc.DownloadDataCompleted += new DownloadDataCompletedEventHandler(callback);
+ url = FixUrl(url);
+ wc.DownloadDataAsync(new Uri(url));
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/HelperWeb.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/HelperWeb.cs.meta
new file mode 100644
index 00000000..a3ad8f6e
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/HelperWeb.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 907a3ff8fb013c44f8e504603af2a4a6
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Locale.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Locale.cs
new file mode 100644
index 00000000..60c16178
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Locale.cs
@@ -0,0 +1,162 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEditor;
+using UnityEngine;
+
+namespace Thry
+{
+ public class Locale
+ {
+ const string EDITOR_LOCALE_NAME = "thry_editor_locale";
+
+ private string[] languages;
+ public int selected_locale_index = 0;
+ private Dictionary<string, string[]> dictionary;
+
+ public Locale(string file_name)
+ {
+ LoadCSV(file_name);
+ }
+
+ public Locale(string file_name, string selected_name)
+ {
+ LoadCSV(file_name);
+ SetSelectedLocale(selected_name);
+ }
+
+ public void SetSelectedLocale(string name)
+ {
+ for (int i = 0; i < languages.Length; i++)
+ if (languages[i].Equals(name))
+ selected_locale_index = i;
+ }
+
+ public string Get(string key)
+ {
+ if(dictionary.ContainsKey(key)) return dictionary[key][selected_locale_index];
+ Debug.LogWarning("Locale[key] could not be found.");
+ return key;
+ }
+
+ public bool Constains(string key)
+ {
+ return dictionary.ContainsKey(key) && string.IsNullOrEmpty(dictionary[key][selected_locale_index]) == false;
+ }
+
+ public string[] available_locales
+ {
+ get
+ {
+ return languages;
+ }
+ }
+
+ public Dictionary<string,string[]>.KeyCollection GetAllKeys()
+ {
+ return dictionary.Keys;
+ }
+
+ public void LoadCSV(string file_name)
+ {
+ List<string> files = UnityHelper.FindAssetsWithFilename(file_name + ".csv");
+ if (files.Count > 0)
+ ParseCSV(FileHelper.ReadFileIntoString(files[0]));
+ else
+ throw new System.Exception("CVS File with name \"" + file_name + "\" could not be found.");
+ }
+
+ private static Locale p_editor;
+ public static Locale editor
+ {
+ get
+ {
+ if (p_editor == null)
+ p_editor = new Locale(EDITOR_LOCALE_NAME);
+ return p_editor;
+ }
+ }
+
+ private void ParseCSV(string text)
+ {
+ List<List<string>> lines = GetCVSFields(text);
+ InitLanguages(lines);
+ lines.RemoveAt(0);
+ InitDictionary(lines);
+ }
+
+ private void InitLanguages(List<List<string>> lines)
+ {
+ languages = new string[lines[0].Count - 1];
+ for (int i = 0; i < languages.Length; i++)
+ languages[i] = lines[0][i + 1];
+ }
+
+ private void InitDictionary(List<List<string>> lines)
+ {
+ dictionary = new Dictionary<string, string[]>();
+ foreach(List<string> line in lines)
+ {
+ string key = line[0];
+ if (key == "")
+ continue;
+ string[] value = new string[languages.Length];
+ value[0] = "";
+ for(int i = 0; i < value.Length; i++)
+ {
+ if (line.Count > i + 1 && line[i + 1] != "")
+ value[i] = line[i + 1];
+ else
+ value[i] = value[0];
+ value[i] = value[i].Replace("\\n", "\n");
+ }
+ dictionary.Add(key, value);
+ }
+ }
+
+ private static List<List<string>> GetCVSFields(string text)
+ {
+ char[] array = text.ToCharArray();
+ List<List<string>> lines = new List<List<string>>();
+ List<string> current_line = new List<string>();
+ lines.Add(current_line);
+ string current_value = "";
+ bool in_apostrpoh = false;
+ for (int i = 0; i < array.Length; i++)
+ {
+ if (!in_apostrpoh && (array[i] == '\r') && i + 1 < array.Length && (array[i + 1] == '\n'))
+ i += 1;
+ if (!in_apostrpoh && (array[i] == '\n'))
+ {
+ current_line.Add(current_value);
+ current_line = new List<string>();
+ lines.Add(current_line);
+ current_value = "";
+ }
+ else if (!in_apostrpoh && array[i] == ',')
+ {
+ current_line.Add(current_value);
+ current_value = "";
+ }
+ else if (!in_apostrpoh && array[i] == '"')
+ {
+ in_apostrpoh = true;
+ }
+ else if (in_apostrpoh && array[i] == '"' && (i == array.Length - 1 || array[i + 1] != '"'))
+ {
+ in_apostrpoh = false;
+ }
+ else if (in_apostrpoh && array[i] == '"' && array[i + 1] == '"')
+ {
+ current_value += '"';
+ i += 1;
+ }
+ else
+ {
+ current_value += array[i];
+ }
+ }
+ current_line.Add(current_value);
+ return lines;
+ }
+ }
+}
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Locale.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Locale.cs.meta
new file mode 100644
index 00000000..2c2a223c
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Locale.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 75c479cb152ec084c918f69a99c3ac18
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/MaterialLinker.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/MaterialLinker.cs
new file mode 100644
index 00000000..bc663c89
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/MaterialLinker.cs
@@ -0,0 +1,260 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEditor;
+using UnityEngine;
+
+namespace Thry
+{
+ public class MaterialLinker
+ {
+ private static Dictionary<(Material,string), List<Material>> linked_materials;
+
+ private static void Load()
+ {
+ if (linked_materials == null)
+ {
+ linked_materials = new Dictionary<(Material,string), List<Material>>();
+ string raw = FileHelper.ReadFileIntoString(PATH.LINKED_MATERIALS_FILE);
+ string[][] parsed = Parser.ParseToObject<string[][]>(raw);
+ if(parsed!=null)
+ foreach (string[] material_cloud in parsed)
+ {
+ List<Material> materials = new List<Material>();
+ for (int i = 1; i < material_cloud.Length; i++)
+ {
+ string path = AssetDatabase.GUIDToAssetPath(material_cloud[i]);
+ Material m = AssetDatabase.LoadAssetAtPath<Material>(path);
+ if (m != null)
+ materials.Add(m);
+ }
+ foreach (Material m in materials)
+ if(linked_materials.ContainsKey((m, material_cloud[0])) == false)
+ linked_materials.Add((m, material_cloud[0]), materials);
+ }
+ }
+ }
+
+ private static void Save()
+ {
+ List<string[]> save_structre = new List<string[]>();
+ HashSet<(Material,string)> has_already_been_saved = new HashSet<(Material,string)>();
+ foreach (KeyValuePair<(Material,string),List<Material>> link in linked_materials)
+ {
+ if (has_already_been_saved.Contains(link.Key)) continue;
+ string[] value = new string[link.Value.Count + 1];
+ value[0] = link.Key.Item2;
+ int i = 1;
+ foreach (Material m in link.Value) {
+ has_already_been_saved.Add((m, link.Key.Item2));
+ value[i++] = UnityHelper.GetGUID(m);
+ }
+ save_structre.Add(value);
+ }
+ FileHelper.WriteStringToFile(Parser.ObjectToString(save_structre),PATH.LINKED_MATERIALS_FILE);
+ }
+
+ public static bool IsLinked(MaterialProperty p)
+ {
+ Load();
+ return linked_materials.ContainsKey(((Material)p.targets[0], p.name));
+ }
+
+ public static List<Material> GetLinked(MaterialProperty p)
+ {
+ return GetLinked((Material)p.targets[0], p);
+ }
+
+ public static List<Material> GetLinked(Material m, MaterialProperty p)
+ {
+ Load();
+ if (linked_materials.ContainsKey((m,p.name)))
+ return linked_materials[(m,p.name)];
+ return null;
+ }
+
+ public static void Link(Material master, Material add_to, MaterialProperty p)
+ {
+ Load();
+ Debug.Log("link " + master.name + "," + add_to.name);
+ bool containes_key1 = linked_materials.ContainsKey((master,p.name));
+ bool containes_key2 = linked_materials.ContainsKey((add_to,p.name));
+
+ if(containes_key1 && containes_key2)
+ {
+ Unlink(add_to, p);
+ Link(master, add_to, p);
+ return;
+ }
+ else if (containes_key1)
+ AddToListIfMaterialAlreadyLinked(master, add_to, p);
+ else if (containes_key2)
+ AddToListIfMaterialAlreadyLinked(add_to, master, p);
+ else
+ {
+ List<Material> value = new List<Material>();
+ value.Add(master);
+ value.Add(add_to);
+ linked_materials[(master,p.name)] = value;
+ linked_materials[(add_to,p.name)] = value;
+ }
+ }
+
+ private static void AddToListIfMaterialAlreadyLinked(Material existing, Material add, MaterialProperty p)
+ {
+ List<Material> value = linked_materials[(existing,p.name)];
+ value.Add(add);
+ linked_materials[(add,p.name)] = value;
+ }
+
+ public static void Unlink(Material m, MaterialProperty p)
+ {
+ Load();
+ List<Material> value = linked_materials[(m,p.name)];
+ value.Remove(m);
+ linked_materials.Remove((m,p.name));
+ }
+
+ private static void UpdateLinkList(List<Material> new_linked_materials, MaterialProperty p)
+ {
+ var key = (p.targets[0] as Material, p.name);
+ if (linked_materials.ContainsKey(key))
+ {
+ List<Material> old_materials = linked_materials[key];
+ foreach (Material m in old_materials)
+ linked_materials.Remove((m, p.name));
+ }
+ foreach (Material m in new_linked_materials)
+ linked_materials[(m, p.name)] = new_linked_materials;
+ }
+
+ public static void UnlinkAll(Material m)
+ {
+ List<(Material, string)> remove_keys = new List<(Material, string)>();
+ foreach (KeyValuePair<(Material,string), List<Material>> link_cloud in linked_materials)
+ {
+ if (link_cloud.Key.Item1 == m)
+ {
+ link_cloud.Value.Remove(m);
+ remove_keys.Add(link_cloud.Key);
+ }
+ }
+ foreach ((Material, string) k in remove_keys)
+ linked_materials.Remove(k);
+ RemoveEmptyLinks();
+ Save();
+ }
+
+ private static void RemoveEmptyLinks()
+ {
+ List<(Material, string)> remove_keys = new List<(Material, string)>();
+ foreach (KeyValuePair<(Material,string), List<Material>> link_cloud in linked_materials)
+ {
+ if (link_cloud.Value.Count < 2)
+ {
+ link_cloud.Value.Clear();
+ remove_keys.Add(link_cloud.Key);
+ }
+ }
+ foreach ((Material, string) k in remove_keys)
+ linked_materials.Remove(k);
+ }
+
+ private static MaterialLinkerPopupWindow window;
+ public static void Popup(Rect activeation_rect, List<Material> linked_materials, MaterialProperty p)
+ {
+ Vector2 pos = GUIUtility.GUIToScreenPoint(Event.current.mousePosition);
+ pos.x = Mathf.Min(EditorWindow.focusedWindow.position.x + EditorWindow.focusedWindow.position.width - 250, pos.x);
+ pos.y = Mathf.Min(EditorWindow.focusedWindow.position.y + EditorWindow.focusedWindow.position.height - 200, pos.y);
+
+ Load();
+ if (window != null)
+ window.Close();
+ window = ScriptableObject.CreateInstance<MaterialLinkerPopupWindow>();
+ window.position = new Rect(pos.x, pos.y, 250, 200);
+ window.Init(linked_materials, p);
+ window.ShowPopup();
+ }
+
+ private class MaterialLinkerPopupWindow : EditorWindow
+ {
+ private Vector2 scrollPos;
+ private List<Material> linked_materials;
+ private MaterialProperty materialProperty;
+
+ public void Init(List<Material> linked_materials, MaterialProperty p)
+ {
+ if (linked_materials == null)
+ linked_materials = new List<Material>();
+ this.linked_materials = new List<Material>(linked_materials);
+
+ string self_guid = UnityHelper.GetGUID((Material)p.targets[0]);
+ for (int i = this.linked_materials.Count - 1; i >= 0; i--)
+ {
+ if (UnityHelper.GetGUID(this.linked_materials[i]) == self_guid)
+ this.linked_materials.RemoveAt(i);
+ }
+ this.materialProperty = p;
+ }
+
+ public new Vector2 minSize = new Vector2(250, 200);
+
+ void OnGUI()
+ {
+ GUILayout.Label("Linked Materials", EditorStyles.boldLabel);
+ float listMaxHeight = this.position.height - 110;
+ GuiHelper.DrawListField<Material>(linked_materials, listMaxHeight, ref scrollPos);
+ GUILayout.Box("Drag and Drop new Material", EditorStyles.helpBox, GUILayout.MinHeight(30));
+ //Rect drag_rect = GUILayoutUtility.GetLastRect();
+ Rect lastRect = GUILayoutUtility.GetLastRect();
+ Rect drag_rect = new Rect(0, lastRect.y, Screen.width, Screen.height - lastRect.y - 30);
+ Event e = Event.current;
+ if ((e.type == EventType.DragPerform || e.type == EventType.DragUpdated) && drag_rect.Contains(e.mousePosition))
+ {
+ DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
+ if (e.type == EventType.DragPerform)
+ {
+ DragAndDrop.AcceptDrag();
+ HanldeDropEvent();
+ }
+ }
+ if (GUI.Button(new Rect(0,this.position.height-30,this.position.width,30),"Done"))
+ this.Close();
+ }
+
+ public void HanldeDropEvent()
+ {
+ foreach (string path in DragAndDrop.paths)
+ {
+ if (AssetDatabase.GetMainAssetTypeAtPath(path) == typeof(Material))
+ {
+ linked_materials.Add(AssetDatabase.LoadAssetAtPath<Material>(path));
+ }
+ }
+ }
+
+ void Awake()
+ {
+
+ }
+
+ void OnDestroy()
+ {
+ //add itself
+ bool contains_itself = false;
+ string self_guid = UnityHelper.GetGUID((Material)materialProperty.targets[0]);
+ for (int i = linked_materials.Count - 1; i >= 0; i--)
+ {
+ if (UnityHelper.GetGUID(linked_materials[i]) == self_guid)
+ contains_itself = true;
+ if (linked_materials[i] == null)
+ linked_materials.RemoveAt(i);
+ }
+ if (linked_materials.Count>0 && !contains_itself)
+ linked_materials.Add((Material)materialProperty.targets[0]);
+
+ UpdateLinkList(linked_materials, materialProperty);
+ Save();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/MaterialLinker.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/MaterialLinker.cs.meta
new file mode 100644
index 00000000..1d1143b9
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/MaterialLinker.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 811f88ca7752d5f458c12b5864202fc9
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Mediator.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Mediator.cs
new file mode 100644
index 00000000..78f877ca
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Mediator.cs
@@ -0,0 +1,25 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace Thry
+{
+ public class Mediator
+ {
+
+ private static Material m_copy;
+ public static Material copy_material
+ {
+ set
+ {
+ m_copy = value;
+ }
+ get
+ {
+ return m_copy;
+ }
+ }
+
+ public static ShaderPart transfer_group;
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Mediator.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Mediator.cs.meta
new file mode 100644
index 00000000..66139bf8
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Mediator.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 404e4ca46c4f1c045a13674ca45d73d4
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ModuleHandler.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ModuleHandler.cs
new file mode 100644
index 00000000..c56ed49f
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ModuleHandler.cs
@@ -0,0 +1,367 @@
+// Material/Shader Inspector for Unity 2017/2018
+// Copyright (C) 2019 Thryrallo
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using UnityEditor;
+using UnityEngine;
+
+namespace Thry
+{
+ public abstract class ModuleSettings
+ {
+ public const string MODULES_CONFIG = "Thry/modules_config";
+
+ public abstract void Draw();
+ }
+
+ public class ModuleHandler
+ {
+ private static List<Module> first_party_modules;
+ private static List<Module> third_party_modules;
+ private static bool modules_are_being_loaded = false;
+
+ private class ModuleCollectionInfo
+ {
+ public string id = null;
+ public string url = null;
+ public string author = null;
+ }
+
+ private class ModuleCollection
+ {
+ public List<ModuleCollectionInfo> first_party = null;
+ public List<ModuleCollectionInfo> third_party = null;
+ }
+
+ public static List<Module> GetFirstPartyModules()
+ {
+ if (!modules_are_being_loaded)
+ LoadModules();
+ return first_party_modules;
+ }
+
+ public static void ForceReloadModules()
+ {
+ LoadModules();
+ }
+
+ public static List<Module> GetThirdPartyModules()
+ {
+ if (!modules_are_being_loaded)
+ LoadModules();
+ return third_party_modules;
+ }
+
+ private static void LoadModules()
+ {
+ modules_are_being_loaded = true;
+ WebHelper.DownloadStringASync(URL.MODULE_COLLECTION, delegate (string s) {
+ first_party_modules = new List<Module>();
+ third_party_modules = new List<Module>();
+ ModuleCollection module_collection = Parser.ParseToObject<ModuleCollection>(s);
+ foreach(ModuleCollectionInfo info in module_collection.first_party)
+ {
+ LoadModule(info,first_party_modules);
+ }
+ foreach (ModuleCollectionInfo info in module_collection.third_party)
+ {
+ LoadModule(info, third_party_modules);
+ }
+ });
+ }
+
+ private static void LoadModule(ModuleCollectionInfo info, List<Module> modules)
+ {
+ WebHelper.DownloadStringASync(info.url, delegate (string data)
+ {
+ Module new_module = new Module();
+ new_module.url = info.url;
+ new_module.author = info.author;
+ new_module.id = info.id;
+ new_module.available_module = Parser.ParseToObject<ModuleInfo>(data);
+ new_module.available_module.version = new_module.available_module.version.Replace(",", ".");
+ bool module_installed = LoadModuleLocationData(new_module);
+ if (module_installed)
+ InitInstalledModule(new_module);
+ else if (Helper.ClassWithNamespaceExists(new_module.available_module.classname))
+ CheckForUnregisteredInstall(new_module);
+ if (new_module.installed_module != null)
+ new_module.installed_module.version = new_module.installed_module.version.Replace(",", ".");
+ if (new_module.available_module.requirement != null)
+ new_module.available_requirement_fullfilled = new_module.available_module.requirement.Test();
+ if (new_module.available_requirement_fullfilled && new_module.installed_module != null && Helper.CompareVersions(new_module.installed_module.version, new_module.available_module.version) == 1)
+ new_module.update_available = true;
+ modules.Add(new_module);
+ UnityHelper.RepaintEditorWindow<Settings>();
+ });
+ }
+
+ private static bool LoadModuleLocationData(Module m)
+ {
+ string data = FileHelper.LoadValueFromFile(m.id,PATH.MODULES_LOCATION__DATA);
+ if (string.IsNullOrEmpty(data))
+ {
+ return false;
+ }
+ m.location_data = Parser.ParseToObject<ModuleLocationData>(data);
+ if (AssetDatabase.GUIDToAssetPath(m.location_data.guid) == "")
+ {
+ m.location_data = null;
+ return false;
+ }
+ return true;
+ }
+
+ private static void SaveModuleLocationData(Module m, string guid)
+ {
+ ModuleLocationData locationData = new ModuleLocationData();
+ locationData.guid = guid;
+ locationData.classname = m.installed_module.classname;
+ locationData.files = m.installed_module.files.ToArray();
+ FileHelper.SaveValueToFile(m.id, Parser.ObjectToString(locationData), PATH.MODULES_LOCATION__DATA);
+ }
+
+ private static void CheckForUnregisteredInstall(Module module)
+ {
+ //Debug.Log(module.available_module.classname + ":" + Helper.ClassWithNamespaceExists(module.available_module.classname));
+ if (Helper.ClassWithNamespaceExists(module.available_module.classname))
+ {
+ module.path = ResolveFilesToDirectory(module.available_module.files.ToArray());
+ if (string.IsNullOrEmpty(module.path) == false)
+ {
+ module.installed_module = Parser.ParseToObject<ModuleInfo>(FileHelper.ReadFileIntoString(FindModuleFilePath(module.path)));
+ SaveModuleLocationData(module,AssetDatabase.AssetPathToGUID(module.path));
+ }
+ }
+ }
+
+ //TODO save location data on install
+ // delete location data on remove
+ // destingish between public and private modules
+
+ private static void InitInstalledModule(Module m)
+ {
+ bool remove = false;
+ if (Helper.ClassWithNamespaceExists(m.location_data.classname))
+ {
+ m.path = GetModuleDirectory(m);
+ if (string.IsNullOrEmpty(m.path) == false)
+ {
+ m.installed_module = Parser.ParseToObject<ModuleInfo>(FileHelper.ReadFileIntoString(FindModuleFilePath(m.path)));
+ string calced_guid = AssetDatabase.AssetPathToGUID(m.path);
+ if (m.location_data.guid != calced_guid)
+ SaveModuleLocationData(m, calced_guid);
+ }
+ else
+ {
+ remove = true;
+ }
+ }
+ if (remove)
+ {
+ FileHelper.RemoveValueFromFile(m.id, PATH.MODULES_LOCATION__DATA);
+ m.location_data = null;
+ }
+ }
+
+ private static string GetModuleDirectory(Module m)
+ {
+ string path = null;
+ if(m.location_data != null)
+ {
+ path = AssetDatabase.GUIDToAssetPath(m.location_data.guid);
+ if(path == "" || path == null || !Directory.Exists(path))
+ {
+ path = ResolveFilesToDirectory(m.location_data.files);
+ }
+ }
+ if (!Directory.Exists(path))
+ path = null;
+ return path;
+ }
+
+ private static string ResolveFilesToDirectory(string[] files)
+ {
+ Dictionary<string, int> path_refernces = new Dictionary<string, int>();
+ foreach (string file in files)
+ {
+ string[] refernces = ResolveFilesToDirectoryFindAllReferneces(file);
+ foreach(string p in refernces)
+ {
+ string found_dir = p.Replace(file, "");
+ if (path_refernces.ContainsKey(found_dir))
+ path_refernces[found_dir] = path_refernces[found_dir] + 1;
+ else
+ path_refernces[found_dir] = 1;
+ }
+ }
+ int most_refernces = 0;
+ string path = null;
+ foreach(KeyValuePair<string,int> pair in path_refernces)
+ {
+ if (pair.Value > most_refernces)
+ {
+ most_refernces = pair.Value;
+ path = pair.Key;
+ }
+ }
+ return path;
+ }
+
+ private static string[] ResolveFilesToDirectoryFindAllReferneces(string file_sub_path)
+ {
+ List<string> valid_paths = new List<string>();
+ string[] found_paths = UnityHelper.FindAssetsWithFilename(Path.GetFileName(file_sub_path)).ToArray();
+ foreach (string p in found_paths)
+ {
+ if (p.EndsWith(file_sub_path))
+ valid_paths.Add(p);
+ }
+ return valid_paths.ToArray();
+ }
+
+ private static string FindModuleFilePath(string directory_path)
+ {
+ string module_path = null;
+ int likelyness = -1;
+ foreach(string f in Directory.GetFiles(directory_path)){
+ string file_name = Path.GetFileName(f);
+ int l = 0;
+ if (file_name.Contains("module")) l++;
+ if (file_name.Contains("thry")) l++;
+ if (file_name.Contains(".json")) l++;
+ if (l > likelyness)
+ {
+ likelyness = l;
+ module_path = f;
+ }
+ }
+ return module_path;
+ }
+
+ public static void InstallRemoveModule(Module module, bool install)
+ {
+ if (install && module.installed_module == null)
+ InstallModule(module);
+ else if (!install && module.installed_module != null)
+ RemoveModule(module);
+ }
+
+ public static void OnCompile()
+ {
+ string url = FileHelper.LoadValueFromFile("update_module_url", PATH.AFTER_COMPILE_DATA);
+ string id = FileHelper.LoadValueFromFile("update_module_id", PATH.AFTER_COMPILE_DATA);
+ if (url != null && url.Length > 0 && id != null && id.Length > 0)
+ {
+ InstallModule(url, id);
+ FileHelper.SaveValueToFile("update_module_id", "", PATH.AFTER_COMPILE_DATA);
+ FileHelper.SaveValueToFile("update_module_url", "", PATH.AFTER_COMPILE_DATA);
+ }
+ }
+
+ public static void UpdateModule(Module module)
+ {
+ module.is_being_installed_or_removed = true;
+ FileHelper.SaveValueToFile("update_module_url", module.url, PATH.AFTER_COMPILE_DATA);
+ FileHelper.SaveValueToFile("update_module_id", module.id, PATH.AFTER_COMPILE_DATA);
+ RemoveModule(module);
+ }
+
+ public static void InstallModule(string url, string id)
+ {
+ WebHelper.DownloadStringASync(url, delegate (string data)
+ {
+ Module new_module = new Module();
+ new_module.url = url;
+ new_module.id = id;
+ new_module.available_module = Parser.ParseToObject<ModuleInfo>(data);
+ InstallModule(new_module);
+ });
+ }
+
+ public static void InstallModule(Module module)
+ {
+ module.is_being_installed_or_removed = true;
+ string temp_path = InstallModuleGetTempDir(module);
+ InstallModuleDownloadFiles(module,temp_path);
+ }
+
+ private static string InstallModuleGetTempDir(Module module)
+ {
+ return "temp_module_" + module.id;
+ }
+
+ private static void InstallModuleDownloadFiles(Module module, string temp_path)
+ {
+ EditorUtility.DisplayProgressBar(module.available_module.name+ " download progress", "", 0);
+ string base_url = Path.GetDirectoryName(module.url);
+ int i = 0;
+ foreach (string file_path in module.available_module.files)
+ {
+ WebHelper.DownloadFileASync(base_url + "/"+ file_path, temp_path + "/" + file_path, delegate (string data)
+ {
+ i++;
+ EditorUtility.DisplayProgressBar("Downloading files for " + module.available_module.name, "Downloaded " + base_url + file_path, (float)i / module.available_module.files.Count);
+ if (i == module.available_module.files.Count)
+ {
+ EditorUtility.ClearProgressBar();
+ InstallModuleFilesDownloaded(module,temp_path);
+ }
+ });
+ }
+ }
+
+ private static void InstallModuleFilesDownloaded(Module module, string temp_dir)
+ {
+ string modules_path = "Assets/thry_modules";
+ if (!Directory.Exists(modules_path))
+ AssetDatabase.CreateFolder("Assets", "thry_modules");
+ string install_path = modules_path + "/" + module.id;
+ module.installed_module = module.available_module;
+ string guid = AssetDatabase.CreateFolder(modules_path, module.id);
+ SaveModuleLocationData(module,guid);
+
+ FileHelper.WriteStringToFile(Parser.ObjectToString(module.available_module), temp_dir + "/module.json");
+ foreach(string d in Directory.GetDirectories(temp_dir))
+ {
+ Directory.Move(d, install_path + "/" + Path.GetFileName(d));
+ }
+ foreach (string f in Directory.GetFiles(temp_dir))
+ {
+ File.Move(f, install_path + "/" + Path.GetFileName(f));
+ }
+ Directory.Delete(temp_dir);
+ AssetDatabase.Refresh();
+ }
+
+ public static void RemoveModule(Module module)
+ {
+ module.is_being_installed_or_removed = true;
+ FileHelper.RemoveValueFromFile(module.id, PATH.MODULES_LOCATION__DATA);
+ foreach (Action f in pre_module_remove_functions)
+ f.Invoke();
+ TrashHandler.MoveDirectoryToTrash(module.path);
+ AssetDatabase.Refresh();
+ }
+
+ private static List<Action> pre_module_remove_functions = new List<Action>();
+
+ public static void RegisterPreModuleRemoveFunction(Action function)
+ {
+ pre_module_remove_functions.Add(function);
+ }
+
+ public static void OnEditorRemove()
+ {
+ string dir_path = ShaderEditor.GetShaderEditorDirectoryPath() + "/thry_modules";
+ if (Directory.Exists(dir_path))
+ TrashHandler.MoveDirectoryToTrash(dir_path);
+ }
+ }
+
+} \ No newline at end of file
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ModuleHandler.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ModuleHandler.cs.meta
new file mode 100644
index 00000000..0303f8b7
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ModuleHandler.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 8349089a9d8124c4bb52b5d414ddca2a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Parser.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Parser.cs
new file mode 100644
index 00000000..edf95aa9
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Parser.cs
@@ -0,0 +1,408 @@
+// Material/Shader Inspector for Unity 2017/2018
+// Copyright (C) 2019 Thryrallo
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text.RegularExpressions;
+using UnityEditor;
+using UnityEngine;
+
+namespace Thry
+{
+ public class Parser
+ {
+
+ public static string Serialize(object o)
+ {
+ return Parser.ObjectToString(o);
+ }
+
+ public static T Deserialize<T>(string s)
+ {
+ return ParseToObject<T>(s);
+ }
+
+ public static string ObjectToString(object obj)
+ {
+ if (obj == null) return "null";
+ if (Helper.IsPrimitive(obj.GetType())) return SerializePrimitive(obj);
+ if (obj is IList) return SerializeList(obj);
+ if (obj.GetType().IsGenericType && obj.GetType().GetGenericTypeDefinition() == typeof(Dictionary<,>)) return SerializeDictionary(obj);
+ if (obj.GetType().IsArray) return SerializeList(obj);
+ if (obj.GetType().IsEnum) return obj.ToString();
+ if (obj.GetType().IsClass) return SerializeClass(obj);
+ if (obj.GetType().IsValueType && !obj.GetType().IsEnum) return SerializeClass(obj);
+ return "";
+ }
+
+ public static T ParseToObject<T>(string s)
+ {
+ object parsed = ParseJson(s);
+ object ret = null;
+ try
+ {
+ ret = (T)ParsedToObject(parsed, typeof(T));
+ }
+ catch (Exception e)
+ {
+ Debug.LogWarning(e.ToString());
+ Debug.LogWarning(s + " cannot be parsed to object of type " + typeof(T).ToString());
+ ret = Activator.CreateInstance(typeof(T));
+ }
+ return (T)ret;
+ }
+
+ //Parser methods
+
+ public static object ParseJson(string input)
+ {
+ //input = input.Replace("\\n", "\n");
+ return ParseJsonPart(input);
+ }
+
+ private static object ParseJsonPart(string input)
+ {
+ input = input.Trim();
+ if (input.StartsWith("{"))
+ return ParseObject(input);
+ else if (input.StartsWith("["))
+ return ParseArray(input);
+ else
+ return ParsePrimitive(input);
+ }
+
+ private static Dictionary<object, object> ParseObject(string input)
+ {
+ input = input.TrimStart(new char[] { '{' });
+ int depth = 0;
+ int variableStart = 0;
+ bool isString = false;
+ Dictionary<object, object> variables = new Dictionary<object, object>();
+ for (int i = 0; i < input.Length; i++)
+ {
+ bool escaped = i != 0 && input[i - 1] == '\\';
+ if (input[i] == '\"' && !escaped)
+ isString = !isString;
+ if (!isString)
+ {
+ if (i == input.Length - 1 || (depth == 0 && input[i] == ',' && !escaped) || (!escaped && depth == 0 && input[i] == '}'))
+ {
+ string[] parts = input.Substring(variableStart, i - variableStart).Split(new char[] { ':' }, 2);
+ if (parts.Length < 2)
+ break;
+ string key = "" + ParseJsonPart(parts[0].Trim());
+ object value = ParseJsonPart(parts[1]);
+ variables.Add(key, value);
+ variableStart = i + 1;
+ }
+ else if ((input[i] == '{' || input[i] == '[') && !escaped)
+ depth++;
+ else if ((input[i] == '}' || input[i] == ']') && !escaped)
+ depth--;
+ }
+
+ }
+ return variables;
+ }
+
+ private static List<object> ParseArray(string input)
+ {
+ input = input.Trim(new char[] { ' ' });
+ int depth = 0;
+ int variableStart = 1;
+ List<object> variables = new List<object>();
+ for (int i = 1; i < input.Length; i++)
+ {
+ if (i == input.Length - 1 || (depth == 0 && input[i] == ',' && (i == 0 || input[i - 1] != '\\')))
+ {
+ variables.Add(ParseJsonPart(input.Substring(variableStart, i - variableStart)));
+ variableStart = i + 1;
+ }
+ else if (input[i] == '{' || input[i] == '[')
+ depth++;
+ else if (input[i] == '}' || input[i] == ']')
+ depth--;
+ }
+ return variables;
+ }
+
+ private static object ParsePrimitive(string input)
+ {
+ if (input.StartsWith("\""))
+ return input.Trim(new char[] { '"' });
+ else if (input.ToLower() == "true")
+ return true;
+ else if (input.ToLower() == "false")
+ return false;
+ else if (input == "null" || input == "NULL" || input == "Null")
+ return null;
+ else
+ {
+ string floatInput = input.Replace(",", ".");
+ if (System.Globalization.CultureInfo.CurrentCulture.NumberFormat.CurrencyDecimalSeparator == ",")
+ floatInput = input.Replace(".", ",");
+ float floatValue;
+ if (float.TryParse(floatInput, out floatValue))
+ {
+ if ((int)floatValue == floatValue)
+ return (int)floatValue;
+ return floatValue;
+ }
+ }
+ return input;
+ }
+
+ //converter methods
+
+ public static string GlobalizationFloat(string s)
+ {
+ s = s.Replace(",", ".");
+ if (System.Globalization.CultureInfo.CurrentCulture.NumberFormat.CurrencyDecimalSeparator == ",")
+ s = s.Replace(".", ",");
+ return s;
+ }
+
+ public static float ParseFloat(string s, float defaultF = 0)
+ {
+ s = GlobalizationFloat(s);
+ float f = defaultF;
+ float.TryParse(s, out f);
+ return f;
+ }
+
+ public static type ConvertParsedToObject<type>(object parsed)
+ {
+ return (type)ParsedToObject(parsed, typeof(type));
+ }
+
+ private static object ParsedToObject(object parsed,Type objtype)
+ {
+ if (parsed == null) return null;
+ if (Helper.IsPrimitive(objtype)) return ConvertToPrimitive(parsed, objtype);
+ if (objtype.IsGenericType && objtype.GetInterfaces().Contains(typeof(IList))) return ConvertToList(parsed, objtype);
+ if (objtype.IsGenericType && objtype.GetGenericTypeDefinition() == typeof(Dictionary<,>)) return ConvertToDictionary(parsed,objtype);
+ if (objtype.IsArray) return ConvertToArray(parsed, objtype);
+ if (objtype.IsEnum) return ConvertToEnum(parsed, objtype);
+ if (objtype.IsClass) return ConvertToObject(parsed, objtype);
+ if (objtype.IsValueType && !objtype.IsEnum) return ConvertToObject(parsed, objtype);
+ return null;
+ }
+
+ private static object ConvertToDictionary(object parsed, Type objtype)
+ {
+ var returnObject = (dynamic)Activator.CreateInstance(objtype);
+ Dictionary<object, object> dict = (Dictionary<object, object>)parsed;
+ foreach (KeyValuePair<object, object> keyvalue in dict)
+ {
+ dynamic key = ParsedToObject(keyvalue.Key, objtype.GetGenericArguments()[0]);
+ dynamic value = ParsedToObject(keyvalue.Value, objtype.GetGenericArguments()[1]);
+ returnObject.Add(key , value );
+ }
+ return returnObject;
+ }
+
+ private static object ConvertToObject(object parsed, Type objtype)
+ {
+ if (parsed.GetType() == typeof(string) && objtype.GetMethod("ParseForThryParser", BindingFlags.Static | BindingFlags.NonPublic) != null)
+ return objtype.GetMethod("ParseForThryParser", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, new object[] { parsed });
+ if (parsed.GetType() != typeof(Dictionary<object, object>)) return null;
+ object returnObject = Activator.CreateInstance(objtype);
+ Dictionary<object, object> dict = (Dictionary<object, object>)parsed;
+ foreach (FieldInfo field in objtype.GetFields())
+ {
+ if (dict.ContainsKey(field.Name))
+ {
+ field.SetValue(returnObject, ParsedToObject(dict[field.Name], field.FieldType));
+ }
+ }
+ foreach (PropertyInfo property in objtype.GetProperties())
+ {
+ if (property.CanWrite && property.CanRead && property.GetIndexParameters().Length == 0 && dict.ContainsKey(property.Name))
+ {
+ property.SetValue(returnObject, ParsedToObject(dict[property.Name], property.PropertyType), null);
+ }
+ }
+ return returnObject;
+ }
+
+ private static object ConvertToList(object parsed, Type objtype)
+ {
+ Type list_obj_type = objtype.GetGenericArguments()[0];
+ List<object> list_strings = (List<object>)parsed;
+ IList return_list = (IList)Activator.CreateInstance(objtype);
+ foreach (object s in list_strings)
+ return_list.Add(ParsedToObject(s, list_obj_type));
+ return return_list;
+ }
+
+ private static object ConvertToArray(object parsed, Type objtype)
+ {
+ if (parsed.GetType() == typeof(string) && objtype.GetMethod("ParseToArrayForThryParser", BindingFlags.Static | BindingFlags.NonPublic) != null)
+ return objtype.GetMethod("ParseToArrayForThryParser", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, new object[] { parsed });
+ if (parsed == null || (parsed is string && (string)parsed == ""))
+ return null;
+ Type array_obj_type = objtype.GetElementType();
+ List<object> list_strings = (List<object>)parsed;
+ IList return_list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(array_obj_type));
+ foreach (object s in list_strings)
+ {
+ object o = ParsedToObject(s, array_obj_type);
+ if(o!=null)
+ return_list.Add(o);
+ }
+ object return_array = Activator.CreateInstance(objtype, return_list.Count);
+ return_list.CopyTo(return_array as Array, 0);
+ return return_array;
+ }
+
+ private static object ConvertToEnum(object parsed, Type objtype)
+ {
+ if (Enum.IsDefined(objtype, (string)parsed))
+ return Enum.Parse(objtype, (string)parsed);
+ Debug.LogWarning("The specified enum for " + objtype.Name + " does not exist. Existing Values are: " + Converter.ArrayToString(Enum.GetValues(objtype)));
+ return Enum.GetValues(objtype).GetValue(0);
+ }
+
+ private static object ConvertToPrimitive(object parsed, Type objtype)
+ {
+ if (typeof(String) == objtype)
+ return parsed!=null?parsed.ToString():null;
+ if (typeof(char) == objtype)
+ return ((string)parsed)[0];
+ return parsed;
+ }
+
+ //Serilizer
+
+ private static string SerializeDictionary(object obj)
+ {
+ string ret = "{";
+ foreach (var item in (dynamic)obj)
+ {
+ object key = item.Key;
+ object val = item.Value;
+ ret += Serialize(key) + ":" + Serialize(val)+",";
+ }
+ ret = ret.TrimEnd(new char[] { ',' });
+ ret += "}";
+ return ret;
+ }
+
+ private static string SerializeClass(object obj)
+ {
+ string ret = "{";
+ foreach(FieldInfo field in obj.GetType().GetFields())
+ {
+ if(field.IsPublic)
+ ret += "\""+field.Name + "\"" + ":" + ObjectToString(field.GetValue(obj)) + ",";
+ }
+ foreach (PropertyInfo property in obj.GetType().GetProperties())
+ {
+ if(property.CanWrite && property.CanRead && property.GetIndexParameters().Length==0)
+ ret += "\""+ property.Name + "\"" + ":" + ObjectToString(property.GetValue(obj,null)) + ",";
+ }
+ ret = ret.TrimEnd(new char[] { ',' });
+ ret += "}";
+ return ret;
+ }
+
+ private static string SerializeList(object obj)
+ {
+ string ret = "[";
+ foreach (object o in obj as IEnumerable)
+ {
+ ret += ObjectToString(o) + ",";
+ }
+ ret = ret.TrimEnd(new char[] { ',' });
+ ret += "]";
+ return ret;
+ }
+
+ private static string SerializePrimitive(object obj)
+ {
+ if (obj.GetType() == typeof(string))
+ return "\"" + obj + "\"";
+ return obj.ToString().Replace(",", "."); ;
+ }
+ }
+
+ public class AnimationParser
+ {
+ public class Animation
+ {
+ public PPtrCurve[] pPtrCurves;
+ }
+
+ public class PPtrCurve
+ {
+ public PPtrType curveType;
+ public PPtrKeyframe[] keyframes;
+ }
+
+ public enum PPtrType
+ {
+ None,Material
+ }
+
+ public class PPtrKeyframe
+ {
+ public float time;
+ public string guid;
+ public int type;
+ }
+
+ public static Animation Parse(AnimationClip clip)
+ {
+ return Parse(AssetDatabase.GetAssetPath(clip));
+ }
+
+ public static Animation Parse(string path)
+ {
+ string data = FileHelper.ReadFileIntoString(path);
+
+ List<PPtrCurve> pPtrCurves = new List<PPtrCurve>();
+ int pptrIndex;
+ int lastIndex = 0;
+ while ((pptrIndex = data.IndexOf("m_PPtrCurves", lastIndex)) != -1)
+ {
+ lastIndex = pptrIndex + 1;
+ int pptrEndIndex = data.IndexOf(" m_", pptrIndex);
+
+ int curveIndex;
+ int lastCurveIndex = pptrIndex;
+ //find all curves
+ while((curveIndex = data.IndexOf(" - curve:", lastCurveIndex, pptrEndIndex- lastCurveIndex)) != -1)
+ {
+ lastCurveIndex = curveIndex + 1;
+ int curveEndIndex = data.IndexOf(" script: ", curveIndex);
+
+ PPtrCurve curve = new PPtrCurve();
+ List<PPtrKeyframe> keyframes = new List<PPtrKeyframe>();
+
+ int keyFrameIndex;
+ int lastKeyFrameIndex = curveIndex;
+ while((keyFrameIndex = data.IndexOf(" - time:", lastKeyFrameIndex, curveEndIndex - lastKeyFrameIndex)) != -1)
+ {
+ lastKeyFrameIndex = keyFrameIndex + 1;
+ int keyFrameEndIndex = data.IndexOf("}", keyFrameIndex);
+
+ PPtrKeyframe keyframe = new PPtrKeyframe();
+ keyframe.time = float.Parse(data.Substring(keyFrameIndex, data.IndexOf("\n", keyFrameIndex, keyFrameEndIndex)));
+ keyframes.Add(keyframe);
+ }
+
+ curve.curveType = data.IndexOf(" attribute: m_Materials", lastKeyFrameIndex, curveEndIndex - lastKeyFrameIndex) != -1 ? PPtrType.Material : PPtrType.None;
+ curve.keyframes = keyframes.ToArray();
+ pPtrCurves.Add(curve);
+ }
+ }
+ Animation animation = new Animation();
+ animation.pPtrCurves = pPtrCurves.ToArray();
+ Debug.Log(Parser.Serialize(animation));
+ return animation;
+ }
+ }
+}
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Parser.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Parser.cs.meta
new file mode 100644
index 00000000..4280d5dd
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Parser.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 3b95745f9a604df4bbc5a00933f516d0
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Presets.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Presets.cs
new file mode 100644
index 00000000..52bdc6f0
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Presets.cs
@@ -0,0 +1,360 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using UnityEditor;
+using UnityEngine;
+
+namespace Thry.ThryEditor
+{
+ public class Presets
+ {
+ const string TAG_IS_PRESET = "isPreset";
+ const string TAG_POSTFIX_IS_PRESET = "_isPreset";
+ const string TAG_PRESET_NAME = "presetName";
+
+ static Dictionary<Material, (Material, Material)> appliedPresets = new Dictionary<Material, (Material, Material)>();
+
+ static string[] p_presetNames;
+ static Material[] p_presetMaterials;
+ static string[] presetNames { get
+ {
+ if (p_presetNames == null)
+ {
+ p_presetMaterials = AssetDatabase.FindAssets("t:material")
+ .Select(g => AssetDatabase.LoadAssetAtPath<Material>(AssetDatabase.GUIDToAssetPath(g)))
+ .Where(m => IsPreset(m)).ToArray();
+ p_presetNames = p_presetMaterials.Select(m => m.GetTag(TAG_PRESET_NAME,false,m.name)).Prepend("").ToArray();
+ }
+ return p_presetNames;
+ }
+ }
+
+ private static PresetsPopupGUI window;
+ public static void PresetGUI(ShaderEditor shaderEditor)
+ {
+ if(GuiHelper.ButtonWithCursor(Styles.icon_style_presets, "Presets", 25, 25))
+ {
+ Event.current.Use();
+ if (Event.current.button == 0)
+ {
+ Vector2 pos = GUIUtility.GUIToScreenPoint(Event.current.mousePosition);
+ pos.x = Mathf.Min(EditorWindow.focusedWindow.position.x + EditorWindow.focusedWindow.position.width - 250, pos.x);
+ pos.y = Mathf.Min(EditorWindow.focusedWindow.position.y + EditorWindow.focusedWindow.position.height - 200, pos.y);
+
+ if (window != null)
+ window.Close();
+ window = ScriptableObject.CreateInstance<PresetsPopupGUI>();
+ window.position = new Rect(pos.x, pos.y, 250, 200);
+ string[] names = presetNames;
+ window.Init(names, p_presetMaterials, shaderEditor);
+ window.titleContent = new GUIContent("Preset List");
+ window.ShowUtility();
+ }
+ else
+ {
+ EditorUtility.DisplayCustomMenu(GUILayoutUtility.GetLastRect(), presetNames.Select(s => new GUIContent(s)).ToArray(), 0, ApplyQuickPreset, shaderEditor);
+ }
+ }
+ }
+
+ static void ApplyQuickPreset(object userData, string[] options, int selected)
+ {
+ Apply(p_presetMaterials[selected - 1], userData as ShaderEditor);
+ }
+
+ public static void PresetEditorGUI(ShaderEditor shaderEditor)
+ {
+ if (shaderEditor.IsPresetEditor)
+ {
+ EditorGUILayout.LabelField(Locale.editor.Get("preset_material_notify"), Styles.greenStyle);
+ string name = shaderEditor.Materials[0].GetTag(TAG_PRESET_NAME, false, "");
+ EditorGUI.BeginChangeCheck();
+ name = EditorGUILayout.TextField(Locale.editor.Get("preset_name"), name);
+ if (EditorGUI.EndChangeCheck())
+ {
+ shaderEditor.Materials[0].SetOverrideTag(TAG_PRESET_NAME, name);
+ p_presetNames = null;
+ }
+ }
+ if (appliedPresets.ContainsKey(shaderEditor.Materials[0]))
+ {
+ if(GUILayout.Button(Locale.editor.Get("preset_revert")+appliedPresets[shaderEditor.Materials[0]].Item1.name))
+ {
+ Revert(shaderEditor);
+ }
+ }
+ }
+
+ public static void Apply(Material preset, ShaderEditor shaderEditor)
+ {
+ appliedPresets[shaderEditor.Materials[0]] = (preset, new Material(shaderEditor.Materials[0]));
+ foreach (ShaderPart prop in shaderEditor.ShaderParts)
+ {
+ if (IsPreset(preset, prop.MaterialProperty))
+ {
+ prop.CopyFromMaterial(preset);
+ }
+ }
+ foreach (Material m in shaderEditor.Materials)
+ MaterialEditor.ApplyMaterialPropertyDrawers(m);
+ }
+
+ static void Revert(ShaderEditor shaderEditor)
+ {
+ Material key = shaderEditor.Materials[0];
+ Material preset = appliedPresets[key].Item1;
+ Material prePreset = appliedPresets[key].Item2;
+ foreach (ShaderPart prop in shaderEditor.ShaderParts)
+ {
+ if (IsPreset(preset, prop.MaterialProperty))
+ {
+ prop.CopyFromMaterial(prePreset);
+ }
+ }
+ foreach (Material m in shaderEditor.Materials)
+ MaterialEditor.ApplyMaterialPropertyDrawers(m);
+ appliedPresets.Remove(key);
+ }
+
+ public static void ApplyList(ShaderEditor shaderEditor, Material[] originals, List<Material> presets)
+ {
+ for(int i=0;i<shaderEditor.Materials.Length && i < originals.Length;i++)
+ shaderEditor.Materials[i].CopyPropertiesFromMaterial(originals[i]);
+ foreach (Material preset in presets)
+ {
+ foreach (ShaderPart prop in shaderEditor.ShaderParts)
+ {
+ if (IsPreset(preset, prop.MaterialProperty))
+ {
+ prop.CopyFromMaterial(preset);
+ }
+ }
+ }
+ MaterialEditor.ApplyMaterialPropertyDrawers(shaderEditor.Materials);
+ shaderEditor.Reload();
+ }
+
+ public static void SetProperty(Material m, MaterialProperty prop, bool value)
+ {
+ m.SetOverrideTag(prop.name + TAG_POSTFIX_IS_PRESET, value?"true":"");
+ }
+
+ public static bool IsPreset(Material m, MaterialProperty prop)
+ {
+ if (prop == null) return false;
+ return m.GetTag(prop.name + TAG_POSTFIX_IS_PRESET, false, "") == "true";
+ }
+
+ public static bool ArePreset(Material[] mats)
+ {
+ return mats.All(m => IsPreset(m));
+ }
+
+ public static bool IsPreset(Material m)
+ {
+ return m.GetTag(TAG_IS_PRESET, false, "false") == "true";
+ }
+
+ [MenuItem("Assets/Thry/Mark as preset")]
+ static void MarkAsPreset()
+ {
+ IEnumerable<Material> mats = Selection.assetGUIDs.Select(g => AssetDatabase.GUIDToAssetPath(g)).
+ Where(p => AssetDatabase.GetMainAssetTypeAtPath(p) == typeof(Material)).Select(p => AssetDatabase.LoadAssetAtPath<Material>(p));
+ foreach (Material m in mats)
+ {
+ m.SetOverrideTag(TAG_IS_PRESET, "true");
+ if (m.GetTag("presetName", false, "") == "") m.SetOverrideTag("presetName", m.name);
+ }
+ p_presetNames = null;
+ }
+
+ [MenuItem("Assets/Thry/Mark as preset", true)]
+ static bool MarkAsPresetValid()
+ {
+ return Selection.assetGUIDs.Select(g => AssetDatabase.GUIDToAssetPath(g)).
+ All(p => AssetDatabase.GetMainAssetTypeAtPath(p) == typeof(Material));
+ }
+
+ [MenuItem("Assets/Thry/Remove as preset")]
+ static void RemoveAsPreset()
+ {
+ IEnumerable<Material> mats = Selection.assetGUIDs.Select(g => AssetDatabase.GUIDToAssetPath(g)).
+ Where(p => AssetDatabase.GetMainAssetTypeAtPath(p) == typeof(Material)).Select(p => AssetDatabase.LoadAssetAtPath<Material>(p));
+ foreach (Material m in mats)
+ {
+ m.SetOverrideTag(TAG_IS_PRESET, "");
+ }
+ p_presetNames = null;
+ }
+
+ [MenuItem("Assets/Thry/Remove as preset", true)]
+ static bool RemoveAsPresetValid()
+ {
+ return Selection.assetGUIDs.Select(g => AssetDatabase.GUIDToAssetPath(g)).
+ All(p => AssetDatabase.GetMainAssetTypeAtPath(p) == typeof(Material));
+ }
+ }
+
+ public class PresetsPopupGUI : EditorWindow
+ {
+ class PresetStruct
+ {
+ public Dictionary<string,PresetStruct> dict;
+ string name;
+ Material preset;
+ bool isOpen = false;
+ bool isOn;
+ public PresetStruct(string name)
+ {
+ this.name = name;
+ dict = new Dictionary<string, PresetStruct>();
+ }
+
+ public PresetStruct GetSubStruct(string name)
+ {
+ name = name.Trim();
+ if (dict.ContainsKey(name) == false)
+ dict.Add(name, new PresetStruct(name));
+ return dict[name];
+ }
+ public void SetPreset(Material m)
+ {
+ preset = m;
+ }
+ public void StructGUI(PresetsPopupGUI popupGUI)
+ {
+ if(preset != null)
+ {
+ EditorGUI.BeginChangeCheck();
+ isOn = EditorGUILayout.ToggleLeft(name, isOn);
+ if (EditorGUI.EndChangeCheck())
+ {
+ popupGUI.ToggelPreset(preset, isOn);
+ }
+ }
+ if(dict.Count > 0)
+ {
+ Rect r = GUILayoutUtility.GetRect(new GUIContent(), Styles.dropDownHeader);
+ r.x = EditorGUI.indentLevel * 15;
+ r.width -= r.x;
+ GUI.Box(r, name, Styles.dropDownHeader);
+ if (Event.current.type == EventType.Repaint)
+ {
+ var toggleRect = new Rect(r.x + 4f, r.y + 2f, 13f, 13f);
+ EditorStyles.foldout.Draw(toggleRect, false, false, isOpen, false);
+ }
+ if (Event.current.type == EventType.MouseDown && GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition))
+ {
+ isOpen = !isOpen;
+ ShaderEditor.Input.Use();
+ }
+ if (isOpen)
+ {
+ EditorGUI.indentLevel += 1;
+ foreach (KeyValuePair<string, PresetStruct> struc in dict)
+ {
+ struc.Value.StructGUI(popupGUI);
+ }
+ EditorGUI.indentLevel -= 1;
+ }
+ }
+
+ }
+
+ public void Reset()
+ {
+ isOn = false;
+ foreach (KeyValuePair<string, PresetStruct> struc in dict)
+ struc.Value.Reset();
+ }
+ }
+
+ Material[] beforePreset;
+ List<Material> tickedPresets = new List<Material>();
+ PresetStruct mainStruct;
+ ShaderEditor shaderEditor;
+ public void Init(string[] names, Material[] presets, ShaderEditor shaderEditor)
+ {
+ this.shaderEditor = shaderEditor;
+ this.beforePreset = shaderEditor.Materials.Select(m => new Material(m)).ToArray();
+ mainStruct = new PresetStruct("");
+ backgroundTextrure = new Texture2D(1,1);
+ if (EditorGUIUtility.isProSkin) backgroundTextrure.SetPixel(0, 0, new Color(0.18f, 0.18f, 0.18f, 1));
+ else backgroundTextrure.SetPixel(0, 0, new Color(0.9f, 0.9f, 0.9f, 1));
+ backgroundTextrure.Apply();
+ for (int i = 1; i < names.Length; i++)
+ {
+ string[] path = names[i].Split('/');
+ PresetStruct addUnder = mainStruct;
+ for (int j=0;j<path.Length; j++)
+ {
+ addUnder = addUnder.GetSubStruct(path[j]);
+ }
+ addUnder.SetPreset(presets[i-1]);
+ }
+ }
+
+ void ToggelPreset(Material m, bool on)
+ {
+ if (tickedPresets.Contains(m) && !on) tickedPresets.Remove(m);
+ if (!tickedPresets.Contains(m) && on) tickedPresets.Add(m);
+ Presets.ApplyList(shaderEditor, beforePreset, tickedPresets);
+ }
+
+ static Texture2D backgroundTextrure;
+
+ Vector2 scroll;
+ bool _save;
+ void OnGUI()
+ {
+ if (mainStruct == null) { this.Close(); return; }
+
+ GUILayout.BeginHorizontal();
+ scroll = GUILayout.BeginScrollView(scroll, GUILayout.Height(position.height - 55));
+
+ GUILayoutUtility.GetRect(10, 5);
+ TopStructGUI();
+
+ GUILayout.EndScrollView();
+ GUILayout.EndHorizontal();
+
+ if (GUI.Button(new Rect(5, this.position.height - 35, this.position.width / 2 - 5, 30), "Apply"))
+ {
+ _save = true;
+ this.Close();
+ }
+
+ if (GUI.Button(new Rect(this.position.width / 2, this.position.height - 35, this.position.width / 2 - 5, 30), "Discard"))
+ {
+ Revert();
+ }
+ }
+ private void OnDestroy()
+ {
+ if (!_save)
+ {
+ Revert();
+ }
+ }
+
+ void TopStructGUI()
+ {
+ foreach (KeyValuePair<string, PresetStruct> struc in mainStruct.dict)
+ {
+ struc.Value.StructGUI(this);
+ }
+ }
+
+ void Revert()
+ {
+ for (int i = 0; i < shaderEditor.Materials.Length; i++)
+ {
+ shaderEditor.Materials[i].CopyPropertiesFromMaterial(beforePreset[i]);
+ MaterialEditor.ApplyMaterialPropertyDrawers(shaderEditor.Materials[i]);
+ }
+ mainStruct.Reset();
+ shaderEditor.Repaint();
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Presets.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Presets.cs.meta
new file mode 100644
index 00000000..5ba3947e
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Presets.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: acdc5aea1214cf246b7fc5df5c5c26a7
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Settings.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Settings.cs
new file mode 100644
index 00000000..98b782cd
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Settings.cs
@@ -0,0 +1,422 @@
+// Material/Shader Inspector for Unity 2017/2018
+// Copyright (C) 2019 Thryrallo
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using UnityEditor;
+using UnityEngine;
+
+namespace Thry
+{
+ public class Settings : EditorWindow
+ {
+
+ public static void firstTimePopup()
+ {
+ Settings window = (Settings)EditorWindow.GetWindow(typeof(Settings));
+ window.isFirstPopop = true;
+ window.Show();
+ }
+
+ public static void updatedPopup(int compare)
+ {
+ Settings window = (Settings)EditorWindow.GetWindow(typeof(Settings));
+ window.updatedVersion = compare;
+ window.Show();
+ }
+
+ public new void Show()
+ {
+ base.Show();
+ this.titleContent = new GUIContent("Thry Settings");
+ }
+
+ public ModuleSettings[] moduleSettings;
+
+ private bool isFirstPopop = false;
+ private int updatedVersion = 0;
+
+ private bool is_init = false;
+
+ public static ButtonData thry_message = null;
+
+ //---------------------Stuff checkers and fixers-------------------
+
+ public void Awake()
+ {
+ InitVariables();
+ }
+
+ private void InitVariables()
+ {
+ List<Type> subclasses = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).Where(type => type.IsSubclassOf(typeof(ModuleSettings))).ToList();
+ moduleSettings = new ModuleSettings[subclasses.Count];
+ int i = 0;
+ foreach(Type classtype in subclasses)
+ {
+ moduleSettings[i++] = (ModuleSettings)Activator.CreateInstance(classtype);
+ }
+
+ is_init = true;
+
+ if (thry_message == null)
+ WebHelper.DownloadStringASync(Thry.URL.SETTINGS_MESSAGE_URL, delegate (string s) { thry_message = Parser.ParseToObject<ButtonData>(s); });
+ }
+
+ //------------------Main GUI
+ void OnGUI()
+ {
+ if (!is_init || moduleSettings==null) InitVariables();
+ GUILayout.Label("ShaderUI v" + Config.Singleton.verion);
+
+ GUINotification();
+ drawLine();
+ GUIMessage();
+ LocaleDropdown();
+ GUIEditor();
+ drawLine();
+ foreach(ModuleSettings s in moduleSettings)
+ {
+ s.Draw();
+ drawLine();
+ }
+ GUIModulesInstalation();
+ }
+
+ //--------------------------GUI Helpers-----------------------------
+
+ private static void drawLine()
+ {
+ Rect rect = EditorGUILayout.GetControlRect(false, 1);
+ rect.height = 1;
+ EditorGUI.DrawRect(rect, new Color(0.5f, 0.5f, 0.5f, 1));
+ }
+
+ private void GUINotification()
+ {
+ if (isFirstPopop)
+ GUILayout.Label(" " + Locale.editor.Get("first_install_message"), Styles.greenStyle);
+ else if (updatedVersion == -1)
+ GUILayout.Label(" " + Locale.editor.Get("update_message"), Styles.greenStyle);
+ else if (updatedVersion == 1)
+ GUILayout.Label(" " + Locale.editor.Get("downgrade_message"), Styles.orangeStyle);
+ }
+
+ private void GUIMessage()
+ {
+ if(thry_message!=null && thry_message.text.Length > 0)
+ {
+ GUIStyle style = new GUIStyle();
+ style.richText = true;
+ style.margin = new RectOffset(7, 0, 0, 0);
+ style.wordWrap = true;
+ GUILayout.Label(new GUIContent(thry_message.text,thry_message.hover), style);
+ Rect r = GUILayoutUtility.GetLastRect();
+ if (Event.current.type == EventType.MouseDown && r.Contains(Event.current.mousePosition))
+ thry_message.action.Perform();
+ drawLine();
+ }
+ }
+
+ bool is_editor_expanded = true;
+ private void GUIEditor()
+ {
+ is_editor_expanded = Foldout(Locale.editor.Get("header_editor"), is_editor_expanded);
+ if (is_editor_expanded)
+ {
+ EditorGUI.indentLevel += 2;
+ Dropdown("default_texture_type");
+ Toggle("showRenderQueue");
+ Toggle("showManualReloadButton");
+
+ EditorGUILayout.Space();
+ Toggle("autoMarkPropertiesAnimated");
+ Toggle("allowCustomLockingRenaming");
+ GUIGradients();
+ EditorGUILayout.Space();
+
+ Toggle("autoSetAnchorOverride");
+ Dropdown("humanBoneAnchor");
+ Text("anchorOverrideObjectName");
+
+ EditorGUI.indentLevel -= 2;
+ }
+ }
+
+ private static void GUIGradients()
+ {
+ GUILayout.BeginHorizontal(GUILayout.ExpandWidth(false));
+ Text("gradient_name", false);
+ string gradient_name = Config.Singleton.gradient_name;
+ if (gradient_name.Contains("<hash>"))
+ GUILayout.Label(Locale.editor.Get("gradient_good_naming"), Styles.greenStyle, GUILayout.ExpandWidth(false));
+ else if (gradient_name.Contains("<material>"))
+ if (gradient_name.Contains("<prop>"))
+ GUILayout.Label(Locale.editor.Get("gradient_good_naming"), Styles.greenStyle, GUILayout.ExpandWidth(false));
+ else
+ GUILayout.Label(Locale.editor.Get("gradient_add_hash_or_prop"), Styles.orangeStyle, GUILayout.ExpandWidth(false));
+ else if (gradient_name.Contains("<prop>"))
+ GUILayout.Label(Locale.editor.Get("gradient_add_material"), Styles.orangeStyle, GUILayout.ExpandWidth(false));
+ else
+ GUILayout.Label(Locale.editor.Get("gradient_add_material_or_prop"), Styles.redStyle, GUILayout.ExpandWidth(false));
+ GUILayout.EndHorizontal();
+ }
+
+ private class TextPopup : EditorWindow
+ {
+ public string text = "";
+ private Vector2 scroll;
+ void OnGUI()
+ {
+ EditorGUILayout.SelectableLabel(Locale.editor.Get("my_data_header"), EditorStyles.boldLabel);
+ Rect last = GUILayoutUtility.GetLastRect();
+
+ Rect data_rect = new Rect(0, last.height, Screen.width, Screen.height - last.height);
+ scroll = EditorGUILayout.BeginScrollView(scroll, GUILayout.Width(data_rect.width), GUILayout.Height(data_rect.height));
+ GUILayout.TextArea(text);
+ EditorGUILayout.EndScrollView();
+ }
+ }
+
+ private void GUIModulesInstalation()
+ {
+ if (ModuleHandler.GetFirstPartyModules() == null)
+ return;
+ if (ModuleHandler.GetFirstPartyModules().Count > 0) {
+ EditorGUILayout.BeginHorizontal();
+ GUILayout.Label(Locale.editor.Get("header_modules"), EditorStyles.boldLabel);
+ if (GUILayout.Button("Reload"))
+ ModuleHandler.ForceReloadModules();
+ EditorGUILayout.EndHorizontal();
+ }
+ bool disabled = ModuleHandler.GetFirstPartyModules().Any(m => m.is_being_installed_or_removed);
+ disabled |= ModuleHandler.GetThirdPartyModules().Any(m => m.is_being_installed_or_removed);
+ EditorGUI.BeginDisabledGroup(disabled);
+ foreach (Module module in ModuleHandler.GetFirstPartyModules())
+ {
+ ModuleUI(module);
+ }
+ GUILayout.Label(Locale.editor.Get("header_thrird_party"), EditorStyles.boldLabel);
+ foreach (Module module in ModuleHandler.GetThirdPartyModules())
+ {
+ ModuleUI(module);
+ }
+ EditorGUI.EndDisabledGroup();
+ }
+
+ private void ModuleUI(Module module)
+ {
+ string text = " " + module.available_module.name;
+ if (module.update_available)
+ text = " " + text;
+ module.ui_expanded = Foldout(text, module.ui_expanded);
+ Rect rect = GUILayoutUtility.GetLastRect();
+ rect.x += 20;
+ rect.y += 1;
+ rect.width = 20;
+ rect.height -= 4;
+
+ bool is_installed = module.installed_module != null;
+
+ EditorGUI.BeginDisabledGroup(!module.available_requirement_fullfilled);
+ EditorGUI.BeginChangeCheck();
+ bool install = GUI.Toggle(rect, is_installed, "");
+ if(EditorGUI.EndChangeCheck()){
+ ModuleHandler.InstallRemoveModule(module, install);
+ }
+ if (module.update_available)
+ {
+ rect.x += 20;
+ rect.width = 55;
+ GUIStyle style = new GUIStyle(EditorStyles.miniButton);
+ style.fixedHeight = 17;
+ if (GUI.Button(rect, "Update",style))
+ ModuleHandler.UpdateModule(module);
+ }
+ //add update notification
+ if (module.ui_expanded)
+ {
+ EditorGUI.indentLevel += 1;
+ ModuleUIDetails(module);
+ EditorGUI.indentLevel -= 1;
+ }
+
+ EditorGUI.EndDisabledGroup();
+ }
+
+ private void ModuleUIDetails(Module module)
+ {
+ float prev_label_width = EditorGUIUtility.labelWidth;
+ EditorGUIUtility.labelWidth = 130;
+
+ EditorGUILayout.HelpBox(module.available_module.description, MessageType.Info);
+ if (module.installed_module != null)
+ EditorGUILayout.LabelField("Installed Version: ", module.installed_module.version);
+ EditorGUILayout.LabelField("Available Version: ", module.available_module.version);
+ if (module.available_module.requirement != null)
+ {
+ if (module.available_requirement_fullfilled)
+ EditorGUILayout.LabelField(Locale.editor.Get("requirements") + ": ", module.available_module.requirement.ToString(), Styles.greenStyle);
+ else
+ EditorGUILayout.LabelField(Locale.editor.Get("requirements") + ": ", module.available_module.requirement.ToString(), Styles.redStyle);
+ }
+ EditorGUILayout.LabelField("Url: ", module.url);
+ if (module.author != null)
+ EditorGUILayout.LabelField("Author: ", module.author);
+
+ EditorGUIUtility.labelWidth = prev_label_width;
+ }
+
+ private static void Text(string configField, bool createHorizontal = true)
+ {
+ Text(configField, Locale.editor.Get(configField), Locale.editor.Get(configField + "_tooltip"), createHorizontal);
+ }
+
+ private static void Text(string configField, string[] content, bool createHorizontal=true)
+ {
+ Text(configField, content[0], content[1], createHorizontal);
+ }
+
+ private static void Text(string configField, string text, string tooltip, bool createHorizontal)
+ {
+ Config config = Config.Singleton;
+ System.Reflection.FieldInfo field = typeof(Config).GetField(configField);
+ if (field != null)
+ {
+ string value = (string)field.GetValue(config);
+ if (createHorizontal)
+ GUILayout.BeginHorizontal(GUILayout.ExpandWidth(false));
+ GUILayout.Space(57);
+ GUILayout.Label(new GUIContent(text, tooltip), GUILayout.ExpandWidth(false));
+ EditorGUI.BeginChangeCheck();
+ value = EditorGUILayout.DelayedTextField("", value, GUILayout.ExpandWidth(false));
+ if (EditorGUI.EndChangeCheck())
+ {
+ field.SetValue(config, value);
+ config.Save();
+ }
+ if (createHorizontal)
+ GUILayout.EndHorizontal();
+ }
+ }
+
+ private static void Toggle(string configField, GUIStyle label_style = null)
+ {
+ Toggle(configField, Locale.editor.Get(configField), Locale.editor.Get(configField + "_tooltip"), label_style);
+ }
+
+ private static void Toggle(string configField, string[] content, GUIStyle label_style = null)
+ {
+ Toggle(configField, content[0], content[1], label_style);
+ }
+
+ private static void Toggle(string configField, string label, string hover, GUIStyle label_style = null)
+ {
+ Config config = Config.Singleton;
+ System.Reflection.FieldInfo field = typeof(Config).GetField(configField);
+ if (field != null)
+ {
+ bool value = (bool)field.GetValue(config);
+ if (Toggle(value, label, hover, label_style) != value)
+ {
+ field.SetValue(config, !value);
+ config.Save();
+ ShaderEditor.RepaintActive();
+ }
+ }
+ }
+
+ private static void Dropdown(string configField)
+ {
+ Dropdown(configField, Locale.editor.Get(configField),Locale.editor.Get(configField+"_tooltip"));
+ }
+
+ private static void Dropdown(string configField, string[] content)
+ {
+ Dropdown(configField, content[0], content[1]);
+ }
+
+ private static void Dropdown(string configField, string label, string hover, GUIStyle label_style = null)
+ {
+ Config config = Config.Singleton;
+ System.Reflection.FieldInfo field = typeof(Config).GetField(configField);
+ if (field != null)
+ {
+ Enum value = (Enum)field.GetValue(config);
+ EditorGUI.BeginChangeCheck();
+ EditorGUILayout.BeginHorizontal();
+ GUILayout.Space(57);
+ GUILayout.Label(new GUIContent(label, hover), GUILayout.ExpandWidth(false));
+ value = EditorGUILayout.EnumPopup(value,GUILayout.ExpandWidth(false));
+ EditorGUILayout.EndHorizontal();
+ if(EditorGUI.EndChangeCheck())
+ {
+ field.SetValue(config, value);
+ config.Save();
+ ShaderEditor.RepaintActive();
+ }
+ }
+ }
+
+ private static void LocaleDropdown()
+ {
+ EditorGUI.BeginChangeCheck();
+ EditorGUILayout.BeginHorizontal();
+ GUILayout.Label(new GUIContent(Locale.editor.Get("locale"), Locale.editor.Get("locale_tooltip")), GUILayout.ExpandWidth(false));
+ Locale.editor.selected_locale_index = EditorGUILayout.Popup(Locale.editor.selected_locale_index, Locale.editor.available_locales, GUILayout.ExpandWidth(false));
+ if(Locale.editor.Get("translator").Length>0)
+ GUILayout.Label(Locale.editor.Get("translation") +": "+Locale.editor.Get("translator"), GUILayout.ExpandWidth(false));
+ EditorGUILayout.EndHorizontal();
+ if(EditorGUI.EndChangeCheck())
+ {
+ Config.Singleton.locale = Locale.editor.available_locales[Locale.editor.selected_locale_index];
+ Config.Singleton.Save();
+ ShaderEditor.ReloadActive();
+ }
+ }
+
+ private static bool Toggle(bool val, string text, GUIStyle label_style = null)
+ {
+ return Toggle(val, text, "",label_style);
+ }
+
+ private static bool Toggle(bool val, string text, string tooltip, GUIStyle label_style=null)
+ {
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(35);
+ val = GUILayout.Toggle(val, new GUIContent("", tooltip), GUILayout.ExpandWidth(false));
+ if(label_style==null)
+ GUILayout.Label(new GUIContent(text, tooltip));
+ else
+ GUILayout.Label(new GUIContent(text, tooltip),label_style);
+ GUILayout.EndHorizontal();
+ return val;
+ }
+
+ private static bool Foldout(string text, bool expanded)
+ {
+ return Foldout(new GUIContent(text), expanded);
+ }
+
+ private static bool Foldout(GUIContent content, bool expanded)
+ {
+ var rect = GUILayoutUtility.GetRect(16f + 20f, 22f, Styles.dropDownHeader);
+ rect = EditorGUI.IndentedRect(rect);
+ GUI.Box(rect, content, Styles.dropDownHeader);
+ var toggleRect = new Rect(rect.x + 4f, rect.y + 2f, 13f, 13f);
+ Event e = Event.current;
+ if (e.type == EventType.Repaint)
+ EditorStyles.foldout.Draw(toggleRect, false, false, expanded, false);
+ if (e.type == EventType.MouseDown && toggleRect.Contains(e.mousePosition) && !e.alt)
+ {
+ expanded = !expanded;
+ e.Use();
+ }
+ return expanded;
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Settings.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Settings.cs.meta
new file mode 100644
index 00000000..a3dce8b5
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Settings.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: bd711b7c02d5bb54ba06a46dbd46f9d4
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ShaderOptimizer.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ShaderOptimizer.cs
new file mode 100644
index 00000000..94b731e2
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ShaderOptimizer.cs
@@ -0,0 +1,2110 @@
+//Original Code from https://github.com/DarthShader/Kaj-Unity-Shaders
+/**MIT License
+
+Copyright (c) 2020 DarthShader
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.**/
+
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEditor;
+using System;
+using System.IO;
+using System.Text.RegularExpressions;
+using System.Text;
+using System.Globalization;
+using System.Linq;
+using System.Security.Cryptography;
+using Object = UnityEngine.Object;
+#if VRC_SDK_VRCSDK3
+using VRC.SDKBase;
+#endif
+#if VRC_SDK_VRCSDK2
+using VRCSDK2;
+#endif
+#if VRC_SDK_VRCSDK2 || VRC_SDK_VRCSDK3
+using VRC.SDKBase.Editor.BuildPipeline;
+#endif
+#if VRC_SDK_VRCSDK3 && !UDON
+using static VRC.SDK3.Avatars.Components.VRCAvatarDescriptor;
+using VRC.SDK3.Avatars.Components;
+using System.Reflection;
+#endif
+// v9
+
+namespace Thry
+{
+
+ public enum LightMode
+ {
+ Always=1,
+ ForwardBase=2,
+ ForwardAdd=4,
+ Deferred=8,
+ ShadowCaster=16,
+ MotionVectors=32,
+ PrepassBase=64,
+ PrepassFinal=128,
+ Vertex=256,
+ VertexLMRGBM=512,
+ VertexLM=1024
+ }
+
+ // Static methods to generate new shader files with in-place constants based on a material's properties
+ // and link that new shader to the material automatically
+ public class ShaderOptimizer
+ {
+ //When locking don't include code from define blocks that are not enabled
+ const bool REMOVE_UNUSED_IF_DEFS = true;
+
+ // For some reason, 'if' statements with replaced constant (literal) conditions cause some compilation error
+ // So until that is figured out, branches will be removed by default
+ // Set to false if you want to keep UNITY_BRANCH and [branch]
+ public static bool RemoveUnityBranches = true;
+
+ // LOD Crossfade Dithing doesn't have multi_compile keyword correctly toggled at build time (its always included) so
+ // this hard-coded material property will uncomment //#pragma multi_compile _ LOD_FADE_CROSSFADE in optimized .shader files
+ public static readonly string LODCrossFadePropertyName = "_LODCrossfade";
+
+ // IgnoreProjector and ForceNoShadowCasting don't work as override tags, so material properties by these names
+ // will determine whether or not //"IgnoreProjector"="True" etc. will be uncommented in optimized .shader files
+ public static readonly string IgnoreProjectorPropertyName = "_IgnoreProjector";
+ public static readonly string ForceNoShadowCastingPropertyName = "_ForceNoShadowCasting";
+
+ // Material property suffix that controls whether the property of the same name gets baked into the optimized shader
+ // e.g. if _Color exists and _ColorAnimated = 1, _Color will not be baked in
+ public static readonly string AnimatedPropertySuffix = "Animated";
+ public static readonly string AnimatedTagSuffix = "Animated";
+
+ // Currently, Material.SetShaderPassEnabled doesn't work on "ShadowCaster" lightmodes,
+ // and doesn't let "ForwardAdd" lights get turned into vertex lights if "ForwardAdd" is simply disabled
+ // vs. if the pases didn't exist at all in the shader.
+ // The Optimizer will take a mask property by this name and attempt to correct these issues
+ // by hard-removing the shadowcaster and fwdadd passes from the shader being optimized.
+ public static readonly string DisabledLightModesPropertyName = "_LightModes";
+
+ // Property that determines whether or not to evaluate KSOInlineSamplerState comments.
+ // Inline samplers can be used to get a wider variety of wrap/filter combinations at the cost
+ // of only having 1x anisotropic filtering on all textures
+ public static readonly string UseInlineSamplerStatesPropertyName = "_InlineSamplerStates";
+ private static bool UseInlineSamplerStates = true;
+
+ // Material properties are put into each CGPROGRAM as preprocessor defines when the optimizer is run.
+ // This is mainly targeted at culling interpolators and lines that rely on those interpolators.
+ // (The compiler is not smart enough to cull VS output that isn't used anywhere in the PS)
+ // Additionally, simply enabling the optimizer can define a keyword, whose name is stored here.
+ // This keyword is added to the beginning of all passes, right after CGPROGRAM
+ public static readonly string OptimizerEnabledKeyword = "OPTIMIZER_ENABLED";
+
+ // Mega shaders are expected to have geometry and tessellation shaders enabled by default,
+ // but with the ability to be disabled by convention property names when the optimizer is run.
+ // Additionally, they can be removed per-lightmode by the given property name plus
+ // the lightmode name as a suffix (e.g. group_toggle_GeometryShadowCaster)
+ // Geometry and Tessellation shaders are REMOVED by default, but if the main gorups
+ // are enabled certain pass types are assumed to be ENABLED
+ public static readonly string GeometryShaderEnabledPropertyName = "GeometryShader_Enabled";
+ public static readonly string TessellationEnabledPropertyName = "Tessellation_Enabled";
+ private static bool UseGeometry = false;
+ private static bool UseGeometryForwardBase = true;
+ private static bool UseGeometryForwardAdd = true;
+ private static bool UseGeometryShadowCaster = true;
+ private static bool UseGeometryMeta = true;
+ private static bool UseTessellation = false;
+ private static bool UseTessellationForwardBase = true;
+ private static bool UseTessellationForwardAdd = true;
+ private static bool UseTessellationShadowCaster = true;
+ private static bool UseTessellationMeta = false;
+
+ // Tessellation can be slightly optimized with a constant max tessellation factor attribute
+ // on the hull shader. A non-animated property by this name will replace the argument of said
+ // attribute if it exists.
+ public static readonly string TessellationMaxFactorPropertyName = "_TessellationFactorMax";
+
+ enum LightModeType { None, ForwardBase, ForwardAdd, ShadowCaster, Meta };
+ private static LightModeType CurrentLightmode = LightModeType.None;
+
+ // In-order list of inline sampler state names that will be replaced by InlineSamplerState() lines
+ public static readonly string[] InlineSamplerStateNames = new string[]
+ {
+ "_linear_repeat",
+ "_linear_clamp",
+ "_linear_mirror",
+ "_linear_mirroronce",
+ "_point_repeat",
+ "_point_clamp",
+ "_point_mirror",
+ "_point_mirroronce",
+ "_trilinear_repeat",
+ "_trilinear_clamp",
+ "_trilinear_mirror",
+ "_trilinear_mirroronce"
+ };
+
+ // Would be better to dynamically parse the "C:\Program Files\UnityXXXX\Editor\Data\CGIncludes\" folder
+ // to get version specific includes but eh
+ public static readonly HashSet<string> DefaultUnityShaderIncludes = new HashSet<string>()
+ {
+ "UnityUI.cginc",
+ "AutoLight.cginc",
+ "GLSLSupport.glslinc",
+ "HLSLSupport.cginc",
+ "Lighting.cginc",
+ "SpeedTreeBillboardCommon.cginc",
+ "SpeedTreeCommon.cginc",
+ "SpeedTreeVertex.cginc",
+ "SpeedTreeWind.cginc",
+ "TerrainEngine.cginc",
+ "TerrainSplatmapCommon.cginc",
+ "Tessellation.cginc",
+ "UnityBuiltin2xTreeLibrary.cginc",
+ "UnityBuiltin3xTreeLibrary.cginc",
+ "UnityCG.cginc",
+ "UnityCG.glslinc",
+ "UnityCustomRenderTexture.cginc",
+ "UnityDeferredLibrary.cginc",
+ "UnityDeprecated.cginc",
+ "UnityGBuffer.cginc",
+ "UnityGlobalIllumination.cginc",
+ "UnityImageBasedLighting.cginc",
+ "UnityInstancing.cginc",
+ "UnityLightingCommon.cginc",
+ "UnityMetaPass.cginc",
+ "UnityPBSLighting.cginc",
+ "UnityShaderUtilities.cginc",
+ "UnityShaderVariables.cginc",
+ "UnityShadowLibrary.cginc",
+ "UnitySprites.cginc",
+ "UnityStandardBRDF.cginc",
+ "UnityStandardConfig.cginc",
+ "UnityStandardCore.cginc",
+ "UnityStandardCoreForward.cginc",
+ "UnityStandardCoreForwardSimple.cginc",
+ "UnityStandardInput.cginc",
+ "UnityStandardMeta.cginc",
+ "UnityStandardParticleInstancing.cginc",
+ "UnityStandardParticles.cginc",
+ "UnityStandardParticleShadow.cginc",
+ "UnityStandardShadow.cginc",
+ "UnityStandardUtils.cginc"
+ };
+
+ public static readonly HashSet<char> ValidSeparators = new HashSet<char>() { ' ', '\t', '\r', '\n', ';', ',', '.', '(', ')', '[', ']', '{', '}', '>', '<', '=', '!', '&', '|', '^', '+', '-', '*', '/', '#' };
+
+ public static readonly HashSet<string> DontRemoveIfBranchesKeywords = new HashSet<string>() { "UNITY_SINGLE_PASS_STEREO", "FORWARD_BASE_PASS", "FORWARD_ADD_PASS", "POINT", "SPOT" };
+ public static readonly HashSet<string> KeywordsUsedByPragmas = new HashSet<string>() { };
+
+ public static readonly string[] ValidPropertyDataTypes = new string[]
+ {
+ "float",
+ "float2",
+ "float3",
+ "float4",
+ "half",
+ "half2",
+ "half3",
+ "half4",
+ "fixed",
+ "fixed2",
+ "fixed3",
+ "fixed4"
+ };
+
+ public static readonly HashSet<string> IllegalPropertyRenames = new HashSet<string>()
+ {
+ "_MainTex",
+ "_Color",
+ "_EmissionColor",
+ "_BumpScale",
+ "_Cutoff",
+ "_DetailNormalMapScale",
+ "_DstBlend",
+ "_GlossMapScale",
+ "_Glossiness",
+ "_GlossyReflections",
+ "_Metallic",
+ "_Mode",
+ "_OcclusionStrength",
+ "_Parallax",
+ "_SmoothnessTextureChannel",
+ "_SpecularHighlights",
+ "_SrcBlend",
+ "_UVSec",
+ "_ZWrite"
+ };
+
+ public static readonly HashSet<string> PropertiesToSkipInMaterialEquallityComparission = new HashSet<string>
+ {
+ "shader_master_label",
+ "shader_is_using_thry_editor"
+ };
+
+ public enum PropertyType
+ {
+ Vector,
+ Float
+ }
+
+ public class PropertyData
+ {
+ public PropertyType type;
+ public string name;
+ public Vector4 value;
+ }
+
+ public class Macro
+ {
+ public string name;
+ public string[] args;
+ public string contents;
+ }
+
+ public class ParsedShaderFile
+ {
+ public string filePath;
+ public string[] lines;
+ }
+
+ public class TextureProperty
+ {
+ public string name;
+ public Texture texture;
+ public int uv;
+ public Vector2 scale;
+ public Vector2 offset;
+ }
+
+ public class GrabPassReplacement
+ {
+ public string originalName;
+ public string newName;
+ }
+
+ public static void CopyAnimatedTagToMaterials(Material[] targets, MaterialProperty source)
+ {
+ string val = (source.targets[0] as Material).GetTag(source.name + AnimatedTagSuffix, false, "");
+ foreach (Material m in targets)
+ {
+ m.SetOverrideTag(source.name+ AnimatedTagSuffix, val);
+ }
+ }
+
+ public static void CopyAnimatedTagFromMaterial(Material source, MaterialProperty target)
+ {
+ string val = source.GetTag(target.name + AnimatedTagSuffix, false, "");
+ foreach (Material m in target.targets)
+ {
+ m.SetOverrideTag(target.name + AnimatedTagSuffix, val);
+ }
+ }
+
+ public static void CopyAnimatedTagFromProperty(MaterialProperty source, MaterialProperty target)
+ {
+ string val = (source.targets[0] as Material).GetTag(source.name + AnimatedTagSuffix, false, "");
+ foreach (Material m in target.targets)
+ {
+ m.SetOverrideTag(target.name + AnimatedTagSuffix, val);
+ }
+ }
+
+ public static void SetAnimatedTag(MaterialProperty prop, string value)
+ {
+ foreach (Material m in prop.targets)
+ {
+ m.SetOverrideTag(prop.name + AnimatedTagSuffix, value);
+ }
+ }
+
+ public static string GetAnimatedTag(MaterialProperty prop)
+ {
+ return (prop.targets[0] as Material).GetTag(prop.name + AnimatedTagSuffix, false, "");
+ }
+
+ public static string GetAnimatedTag(Material m, string prop)
+ {
+ return m.GetTag(prop + AnimatedTagSuffix, false, "");
+ }
+
+ public static string GetRenamedPropertySuffix(Material m)
+ {
+ string cleanedMaterialName = Regex.Replace(m.name.Trim(), @"[^0-9a-zA-Z_]+", string.Empty);
+ if (Config.Singleton.allowCustomLockingRenaming)
+ return m.GetTag("thry_rename_suffix", false, cleanedMaterialName);
+ return cleanedMaterialName;
+ }
+
+ struct RenamingProperty
+ {
+ public MaterialProperty Prop;
+ public string Keyword;
+ public string Replace;
+ public RenamingProperty(MaterialProperty prop, string keyword, string replace)
+ {
+ this.Prop = prop;
+ this.Keyword = keyword;
+ this.Replace = replace;
+ }
+ }
+
+ private static bool Lock(Material material, MaterialProperty[] props, bool applyShaderLater = false)
+ {
+ // File filepaths and names
+ Shader shader = material.shader;
+ string shaderFilePath = AssetDatabase.GetAssetPath(shader);
+ string materialFilePath = AssetDatabase.GetAssetPath(material);
+ string materialFolder = Path.GetDirectoryName(materialFilePath);
+ string guid = AssetDatabase.AssetPathToGUID(materialFilePath);
+ string newShaderName = "Hidden/Locked/" + shader.name + "/" + guid;
+ //string newShaderDirectory = materialFolder + "/OptimizedShaders/" + material.name + "-" + smallguid + "/";
+ string newShaderDirectory = materialFolder + "/OptimizedShaders/" + material.name + "/";
+
+ // suffix for animated properties when renaming is enabled
+ string animPropertySuffix = GetRenamedPropertySuffix(material);
+
+ // Get collection of all properties to replace
+ // Simultaneously build a string of #defines for each CGPROGRAM
+ StringBuilder definesSB = new StringBuilder();
+ // Append convention OPTIMIZER_ENABLED keyword
+ definesSB.Append(Environment.NewLine);
+ definesSB.Append("#define ");
+ definesSB.Append(OptimizerEnabledKeyword);
+ definesSB.Append(Environment.NewLine);
+ // Append all keywords active on the material
+ foreach (string keyword in material.shaderKeywords)
+ {
+ if (keyword == "") continue; // idk why but null keywords exist if _ keyword is used and not removed by the editor at some point
+ definesSB.Append("#define ");
+ definesSB.Append(keyword);
+ definesSB.Append(Environment.NewLine);
+ }
+
+ KeywordsUsedByPragmas.Clear();
+
+ Dictionary<string,bool> removeBetweenKeywords = new Dictionary<string,bool>();
+ List<PropertyData> constantProps = new List<PropertyData>();
+ List<RenamingProperty> animatedPropsToRename = new List<RenamingProperty>();
+ List<RenamingProperty> animatedPropsToDuplicate = new List<RenamingProperty>();
+ foreach (MaterialProperty prop in props)
+ {
+ if (prop == null) continue;
+
+ if (prop.name.Contains("_commentIf"))
+ {
+ if (Regex.IsMatch(prop.name, @".*_commentIfOne_(\d|\w)+") && prop.floatValue == 1)
+ {
+ string key = Regex.Match(prop.name, @"_commentIfOne_(\d|\w)+").Value.Replace("_commentIfOne_", "");
+ removeBetweenKeywords.Add(key, false);
+ }
+ if (Regex.IsMatch(prop.name, @".*_commentIfZero_(\d|\w)+") && prop.floatValue == 0)
+ {
+ string key = Regex.Match(prop.name, @"_commentIfZero_(\d|\w)+").Value.Replace("_commentIfZero_", "");
+ removeBetweenKeywords.Add(key, false);
+ }
+ }
+
+ // Every property gets turned into a preprocessor variable
+ switch (prop.type)
+ {
+ case MaterialProperty.PropType.Float:
+ case MaterialProperty.PropType.Range:
+ definesSB.Append("#define PROP");
+ definesSB.Append(prop.name.ToUpperInvariant());
+ definesSB.Append(' ');
+ definesSB.Append(prop.floatValue.ToString(CultureInfo.InvariantCulture));
+ definesSB.Append(Environment.NewLine);
+ break;
+ case MaterialProperty.PropType.Texture:
+ if (prop.textureValue != null)
+ {
+ definesSB.Append("#define PROP");
+ definesSB.Append(prop.name.ToUpperInvariant());
+ definesSB.Append(Environment.NewLine);
+ }
+ break;
+ }
+
+ if (prop.name.EndsWith(AnimatedPropertySuffix, StringComparison.Ordinal)) continue;
+ else if (prop.name == UseInlineSamplerStatesPropertyName)
+ {
+ UseInlineSamplerStates = (prop.floatValue == 1);
+ continue;
+ }
+ else if (prop.name.StartsWith(GeometryShaderEnabledPropertyName, StringComparison.Ordinal))
+ {
+ if (prop.name == GeometryShaderEnabledPropertyName)
+ UseGeometry = (prop.floatValue == 1);
+ else if (prop.name == GeometryShaderEnabledPropertyName + "ForwardBase")
+ UseGeometryForwardBase = (prop.floatValue == 1);
+ else if (prop.name == GeometryShaderEnabledPropertyName + "ForwardAdd")
+ UseGeometryForwardAdd = (prop.floatValue == 1);
+ else if (prop.name == GeometryShaderEnabledPropertyName + "ShadowCaster")
+ UseGeometryShadowCaster = (prop.floatValue == 1);
+ else if (prop.name == GeometryShaderEnabledPropertyName + "Meta")
+ UseGeometryMeta = (prop.floatValue == 1);
+ }
+ else if (prop.name.StartsWith(TessellationEnabledPropertyName, StringComparison.Ordinal))
+ {
+ if (prop.name == TessellationEnabledPropertyName)
+ UseTessellation = (prop.floatValue == 1);
+ else if (prop.name == TessellationEnabledPropertyName + "ForwardBase")
+ UseTessellationForwardBase = (prop.floatValue == 1);
+ else if (prop.name == TessellationEnabledPropertyName + "ForwardAdd")
+ UseTessellationForwardAdd = (prop.floatValue == 1);
+ else if (prop.name == TessellationEnabledPropertyName + "ShadowCaster")
+ UseTessellationShadowCaster = (prop.floatValue == 1);
+ else if (prop.name == TessellationEnabledPropertyName + "Meta")
+ UseTessellationMeta = (prop.floatValue == 1);
+ }
+
+ string animateTag = material.GetTag(prop.name + AnimatedTagSuffix, false, "");
+ if(string.IsNullOrEmpty(animateTag) == false)
+ {
+ // check if we're renaming the property as well
+ if (animateTag == "2")
+ {
+ if (!prop.name.EndsWith("UV", StringComparison.Ordinal) && !prop.name.EndsWith("Pan", StringComparison.Ordinal)) // this property might be animated, but we're not allowed to rename it. this will break things.
+ {
+ if (IllegalPropertyRenames.Contains(prop.name))
+ animatedPropsToDuplicate.Add(new RenamingProperty(prop, prop.name, prop.name + "_" + animPropertySuffix));
+ else
+ animatedPropsToRename.Add(new RenamingProperty(prop, prop.name, prop.name + "_" + animPropertySuffix));
+ if (prop.type == MaterialProperty.PropType.Texture)
+ {
+ animatedPropsToRename.Add(new RenamingProperty(prop, prop.name + "_ST", prop.name + "_" + animPropertySuffix + "_ST"));
+ animatedPropsToRename.Add(new RenamingProperty(prop, prop.name + "_TexelSize", prop.name + "_" + animPropertySuffix + "_TexelSize"));
+ }
+ }
+ }
+
+ continue;
+ }
+
+ if (prop.displayName.EndsWith("NL", StringComparison.Ordinal)) continue;
+
+ PropertyData propData;
+ switch(prop.type)
+ {
+ case MaterialProperty.PropType.Color:
+ propData = new PropertyData();
+ propData.type = PropertyType.Vector;
+ propData.name = prop.name;
+ if ((prop.flags & MaterialProperty.PropFlags.HDR) != 0)
+ {
+ if ((prop.flags & MaterialProperty.PropFlags.Gamma) != 0)
+ propData.value = prop.colorValue.linear;
+ else propData.value = prop.colorValue;
+ }
+ else if ((prop.flags & MaterialProperty.PropFlags.Gamma) != 0)
+ propData.value = prop.colorValue;
+ else propData.value = prop.colorValue.linear;
+ if (PlayerSettings.colorSpace == ColorSpace.Gamma) propData.value = prop.colorValue;
+ constantProps.Add(propData);
+ break;
+ case MaterialProperty.PropType.Vector:
+ propData = new PropertyData();
+ propData.type = PropertyType.Vector;
+ propData.name = prop.name;
+ propData.value = prop.vectorValue;
+ constantProps.Add(propData);
+ break;
+ case MaterialProperty.PropType.Float:
+ case MaterialProperty.PropType.Range:
+ propData = new PropertyData();
+ propData.type = PropertyType.Float;
+ propData.name = prop.name;
+ propData.value = new Vector4(prop.floatValue, 0, 0, 0);
+ constantProps.Add(propData);
+ break;
+ case MaterialProperty.PropType.Texture:
+ PropertyData ST = new PropertyData();
+ ST.type = PropertyType.Vector;
+ ST.name = prop.name + "_ST";
+ Vector2 offset = material.GetTextureOffset(prop.name);
+ Vector2 scale = material.GetTextureScale(prop.name);
+ ST.value = new Vector4(scale.x, scale.y, offset.x, offset.y);
+ constantProps.Add(ST);
+
+ PropertyData TexelSize = new PropertyData();
+ TexelSize.type = PropertyType.Vector;
+ TexelSize.name = prop.name + "_TexelSize";
+ Texture t = prop.textureValue;
+ if (t != null)
+ TexelSize.value = new Vector4(1.0f / t.width, 1.0f / t.height, t.width, t.height);
+ else TexelSize.value = new Vector4(1.0f, 1.0f, 1.0f, 1.0f);
+ constantProps.Add(TexelSize);
+ break;
+ }
+ }
+ string optimizerDefines = definesSB.ToString();
+
+ // Get list of lightmode passes to delete
+ List<string> disabledLightModes = new List<string>();
+ var disabledLightModesProperty = Array.Find(props, x => x.name == DisabledLightModesPropertyName);
+ if (disabledLightModesProperty != null)
+ {
+ int lightModesMask = (int)disabledLightModesProperty.floatValue;
+ if ((lightModesMask & (int)LightMode.ForwardAdd) != 0)
+ disabledLightModes.Add("ForwardAdd");
+ if ((lightModesMask & (int)LightMode.ShadowCaster) != 0)
+ disabledLightModes.Add("ShadowCaster");
+ }
+
+ // Parse shader and cginc files, also gets preprocessor macros
+ List<ParsedShaderFile> shaderFiles = new List<ParsedShaderFile>();
+ List<Macro> macros = new List<Macro>();
+ if (!ParseShaderFilesRecursive(shaderFiles, newShaderDirectory, shaderFilePath, macros, material, removeBetweenKeywords))
+ return false;
+
+ int commentKeywords = 0;
+
+ List<GrabPassReplacement> grabPassVariables = new List<GrabPassReplacement>();
+ // Loop back through and do macros, props, and all other things line by line as to save string ops
+ // Will still be a massive n2 operation from each line * each property
+ foreach (ParsedShaderFile psf in shaderFiles)
+ {
+ // replace property names when prop is animated
+ for (int i = 0; i < psf.lines.Length; i++)
+ {
+ foreach (var animProp in animatedPropsToRename)
+ {
+ // don't have to match if that prop does not even exist in that line
+ if (psf.lines[i].Contains(animProp.Keyword))
+ {
+ string pattern = animProp.Keyword + @"(?!(\w|\d))";
+ psf.lines[i] = Regex.Replace(psf.lines[i], pattern, animProp.Replace, RegexOptions.Multiline);
+ }
+ }
+ foreach (var animProp in animatedPropsToDuplicate)
+ {
+ if (psf.lines[i].Contains(animProp.Keyword))
+ {
+ //if Line is property definition duplicate it
+ bool isDefinition = Regex.Match(psf.lines[i], animProp.Keyword + @"\s*\(""[^""]+""\s*,\s*\w+\)\s*=").Success;
+ string og = null;
+ if (isDefinition)
+ og = psf.lines[i];
+
+ string pattern = animProp.Keyword + @"(?!(\w|\d))";
+ psf.lines[i] = Regex.Replace(psf.lines[i], pattern, animProp.Replace, RegexOptions.Multiline);
+
+ if (isDefinition)
+ psf.lines[i] = og + "\r\n" + psf.lines[i];
+ }
+ }
+ }
+
+
+ // Shader file specific stuff
+ if (psf.filePath.EndsWith(".shader", StringComparison.Ordinal))
+ {
+ for (int i=0; i<psf.lines.Length;i++)
+ {
+ string trimmedLine = psf.lines[i].TrimStart();
+
+ if (trimmedLine.StartsWith("Shader", StringComparison.Ordinal))
+ {
+ string originalSgaderName = psf.lines[i].Split('\"')[1];
+ psf.lines[i] = psf.lines[i].Replace(originalSgaderName, newShaderName);
+ }
+ else if (trimmedLine.StartsWith("//#pragmamulti_compile_LOD_FADE_CROSSFADE", StringComparison.Ordinal))
+ {
+ MaterialProperty crossfadeProp = Array.Find(props, x => x.name == LODCrossFadePropertyName);
+ if (crossfadeProp != null && crossfadeProp.floatValue == 1)
+ psf.lines[i] = psf.lines[i].Replace("//#pragma", "#pragma");
+ }
+ else if (trimmedLine.StartsWith("//\"IgnoreProjector\"=\"True\"", StringComparison.Ordinal))
+ {
+ MaterialProperty projProp = Array.Find(props, x => x.name == IgnoreProjectorPropertyName);
+ if (projProp != null && projProp.floatValue == 1)
+ psf.lines[i] = psf.lines[i].Replace("//\"IgnoreProjector", "\"IgnoreProjector");
+ }
+ else if (trimmedLine.StartsWith("//\"ForceNoShadowCasting\"=\"True\"", StringComparison.Ordinal))
+ {
+ MaterialProperty forceNoShadowsProp = Array.Find(props, x => x.name == ForceNoShadowCastingPropertyName);
+ if (forceNoShadowsProp != null && forceNoShadowsProp.floatValue == 1)
+ psf.lines[i] = psf.lines[i].Replace("//\"ForceNoShadowCasting", "\"ForceNoShadowCasting");
+ }
+ else if (trimmedLine.StartsWith("GrabPass {", StringComparison.Ordinal))
+ {
+ GrabPassReplacement gpr = new GrabPassReplacement();
+ string[] splitLine = trimmedLine.Split('\"');
+ if (splitLine.Length == 1)
+ gpr.originalName = "_GrabTexture";
+ else
+ gpr.originalName = splitLine[1];
+ gpr.newName = material.GetTag("GrabPass" + grabPassVariables.Count, false, "_GrabTexture");
+ psf.lines[i] = "GrabPass { \"" + gpr.newName + "\" }";
+ grabPassVariables.Add(gpr);
+ }
+ else if (trimmedLine.StartsWith("CGINCLUDE", StringComparison.Ordinal))
+ {
+ for (int j=i+1; j<psf.lines.Length;j++)
+ if (psf.lines[j].TrimStart().StartsWith("ENDCG", StringComparison.Ordinal))
+ {
+ ReplaceShaderValues(material, psf.lines, i+1, j, props, constantProps, macros, grabPassVariables);
+ break;
+ }
+ }
+ else if (trimmedLine.StartsWith("CGPROGRAM", StringComparison.Ordinal))
+ {
+ if(commentKeywords == 0)
+ psf.lines[i] += optimizerDefines;
+ for (int j=i+1; j<psf.lines.Length;j++)
+ if (psf.lines[j].TrimStart().StartsWith("ENDCG", StringComparison.Ordinal))
+ {
+ ReplaceShaderValues(material, psf.lines, i+1, j, props, constantProps, macros, grabPassVariables);
+ break;
+ }
+ }
+ // Lightmode based pass removal, requires strict formatting
+ else if (trimmedLine.StartsWith("Tags", StringComparison.Ordinal))
+ {
+ string lineFullyTrimmed = trimmedLine.Replace(" ", "").Replace("\t", "");
+ // expects lightmode tag to be on the same line like: Tags { "LightMode" = "ForwardAdd" }
+ if (lineFullyTrimmed.Contains("\"LightMode\"=\""))
+ {
+ string lightModeName = lineFullyTrimmed.Split('\"')[3];
+ // Store current lightmode name in a static, useful for per-pass geometry and tessellation removal
+ if (lightModeName == "ForwardBase") CurrentLightmode = LightModeType.ForwardBase;
+ else if (lightModeName == "ForwardAdd") CurrentLightmode = LightModeType.ForwardAdd;
+ else if (lightModeName == "ShadowCaster") CurrentLightmode = LightModeType.ShadowCaster;
+ else if (lightModeName == "Meta") CurrentLightmode = LightModeType.Meta;
+ else CurrentLightmode = LightModeType.None;
+ if (disabledLightModes.Contains(lightModeName))
+ {
+ // Loop up from psf.lines[i] until standalone "Pass" line is found, delete it
+ int j=i-1;
+ for (;j>=0;j--)
+ if (psf.lines[j].Replace(" ", "").Replace("\t", "") == "Pass")
+ break;
+ // then delete each line until a standalone ENDCG line is found
+ for (;j<psf.lines.Length;j++)
+ {
+ if (psf.lines[j].Replace(" ", "").Replace("\t", "") == "ENDCG")
+ break;
+ psf.lines[j] = "";
+ }
+ // then delete each line until a standalone '}' line is found
+ for (;j<psf.lines.Length;j++)
+ {
+ string temp = psf.lines[j];
+ psf.lines[j] = "";
+ if (temp.Replace(" ", "").Replace("\t", "") == "}")
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ else // CGINC file
+ ReplaceShaderValues(material, psf.lines, 0, psf.lines.Length, props, constantProps, macros, grabPassVariables);
+
+ // Recombine file lines into a single string
+ int totalLen = psf.lines.Length*2; // extra space for newline chars
+ foreach (string line in psf.lines)
+ totalLen += line.Length;
+ StringBuilder sb = new StringBuilder(totalLen);
+ // This appendLine function is incompatible with the '\n's that are being added elsewhere
+ foreach (string line in psf.lines)
+ sb.AppendLine(line);
+ string output = sb.ToString();
+
+ //cull shader file path
+ string fileName = Path.GetFileName(psf.filePath);
+ // Write output to file
+ (new FileInfo(newShaderDirectory + fileName)).Directory.Create();
+ try
+ {
+ StreamWriter sw = new StreamWriter(newShaderDirectory + fileName);
+ sw.Write(output);
+ sw.Close();
+ }
+ catch (IOException e)
+ {
+ Debug.LogError("[Shader Optimizer] Processed shader file " + newShaderDirectory + fileName + " could not be written. " + e.ToString());
+ return false;
+ }
+ }
+
+ AssetDatabase.Refresh();
+
+ ApplyStruct applyStruct = new ApplyStruct();
+ applyStruct.material = material;
+ applyStruct.shader = shader;
+ applyStruct.smallguid = guid;
+ applyStruct.newShaderName = newShaderName;
+ applyStruct.animatedPropsToRename = animatedPropsToRename;
+ applyStruct.animatedPropsToDuplicate = animatedPropsToDuplicate;
+ applyStruct.animPropertySuffix = animPropertySuffix;
+
+ if (applyShaderLater)
+ {
+ //Debug.Log("Apply later: "+applyStructsLater.Count+ ", "+material.name);
+ applyStructsLater[material] = applyStruct;
+ return true;
+ }
+ return LockApplyShader(applyStruct);
+ }
+
+ private static Dictionary<Material, ApplyStruct> applyStructsLater = new Dictionary<Material, ApplyStruct>();
+
+ private struct ApplyStruct
+ {
+ public Material material;
+ public Shader shader;
+ public string smallguid;
+ public string newShaderName;
+ public List<RenamingProperty> animatedPropsToRename;
+ public List<RenamingProperty> animatedPropsToDuplicate;
+ public string animPropertySuffix;
+ public bool shared;
+ }
+
+ private static bool LockApplyShader(Material material)
+ {
+ if (applyStructsLater.ContainsKey(material) == false) return false;
+ ApplyStruct applyStruct = applyStructsLater[material];
+ if (applyStruct.shared)
+ {
+ material.shader = applyStruct.material.shader;
+ return true;
+ }
+ //applyStructsLater.Remove(material);
+ return LockApplyShader(applyStruct);
+ }
+
+ private static bool LockApplyShader(ApplyStruct applyStruct)
+ {
+ Material material = applyStruct.material;
+ Shader shader = applyStruct.shader;
+ string newShaderName = applyStruct.newShaderName;
+ List<RenamingProperty> animatedPropsToRename = applyStruct.animatedPropsToRename;
+ List<RenamingProperty> animatedPropsToDuplicate = applyStruct.animatedPropsToDuplicate;
+ string animPropertySuffix = applyStruct.animPropertySuffix;
+
+ // Write original shader to override tag
+ material.SetOverrideTag("OriginalShader", shader.name);
+ // Write the new shader folder name in an override tag so it will be deleted
+
+ // For some reason when shaders are swapped on a material the RenderType override tag gets completely deleted and render queue set back to -1
+ // So these are saved as temp values and reassigned after switching shaders
+ string renderType = material.GetTag("RenderType", false, "");
+ int renderQueue = material.renderQueue;
+
+ // Actually switch the shader
+ Shader newShader = Shader.Find(newShaderName);
+ if (newShader == null)
+ {
+ Debug.LogError("[Shader Optimizer] Generated shader " + newShaderName + " could not be found");
+ return false;
+ }
+ material.shader = newShader;
+ //ShaderEditor.reload();
+ material.SetOverrideTag("RenderType", renderType);
+ material.renderQueue = renderQueue;
+
+ // Remove ALL keywords
+ foreach (string keyword in material.shaderKeywords)
+ if(material.IsKeywordEnabled(keyword)) material.DisableKeyword(keyword);
+
+ foreach (var animProp in animatedPropsToRename)
+ {
+ var newName = animProp.Prop.name + "_" + animPropertySuffix;
+ switch (animProp.Prop.type)
+ {
+ case MaterialProperty.PropType.Color:
+ material.SetColor(newName, animProp.Prop.colorValue);
+ break;
+ case MaterialProperty.PropType.Vector:
+ material.SetVector(newName, animProp.Prop.vectorValue);
+ break;
+ case MaterialProperty.PropType.Float:
+ material.SetFloat(newName, animProp.Prop.floatValue);
+ break;
+ case MaterialProperty.PropType.Range:
+ material.SetFloat(newName, animProp.Prop.floatValue);
+ break;
+ case MaterialProperty.PropType.Texture:
+ material.SetTexture(newName, animProp.Prop.textureValue);
+ material.SetTextureScale(newName, new Vector2(animProp.Prop.textureScaleAndOffset.x, animProp.Prop.textureScaleAndOffset.y));
+ material.SetTextureOffset(newName, new Vector2(animProp.Prop.textureScaleAndOffset.z, animProp.Prop.textureScaleAndOffset.w));
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(material), "This property type should not be renamed and can not be set.");
+ }
+ }
+
+ foreach (var animProp in animatedPropsToDuplicate)
+ {
+ var newName = animProp.Prop.name + "_" + animPropertySuffix;
+ switch (animProp.Prop.type)
+ {
+ case MaterialProperty.PropType.Color:
+ material.SetColor(newName, animProp.Prop.colorValue);
+ break;
+ case MaterialProperty.PropType.Vector:
+ material.SetVector(newName, animProp.Prop.vectorValue);
+ break;
+ case MaterialProperty.PropType.Float:
+ material.SetFloat(newName, animProp.Prop.floatValue);
+ break;
+ case MaterialProperty.PropType.Range:
+ material.SetFloat(newName, animProp.Prop.floatValue);
+ break;
+ case MaterialProperty.PropType.Texture:
+ material.SetTexture(newName, animProp.Prop.textureValue);
+ material.SetTextureScale(newName, new Vector2(animProp.Prop.textureScaleAndOffset.x, animProp.Prop.textureScaleAndOffset.y));
+ material.SetTextureOffset(newName, new Vector2(animProp.Prop.textureScaleAndOffset.z, animProp.Prop.textureScaleAndOffset.w));
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(material), "This property type should not be renamed and can not be set.");
+ }
+ }
+ return true;
+ }
+
+ /** <summary>Find longest common directoy</summary> */
+ public static int GetLongestCommonDirectoryLength(string[] s)
+ {
+ int k = s[0].Length;
+ for (int i = 1; i < s.Length; i++)
+ {
+ k = Math.Min(k, s[i].Length);
+ for (int j = 0; j < k; j++)
+ if ( AreCharsInPathEqual(s[i][j] , s[0][j]) == false)
+ {
+ k = j;
+ break;
+ }
+ }
+ string p = s[0].Substring(0, k);
+ if (Directory.Exists(p)) return p.Length;
+ else return Path.GetDirectoryName(p).Length;
+ }
+
+ private static bool AreCharsInPathEqual(char c1, char c2)
+ {
+ return (c1 == c2) || ((c1 == '/' || c1 == '\\') && (c2 == '/' || c2 == '\\'));
+ }
+
+ // Preprocess each file for macros and includes
+ // Save each file as string[], parse each macro with //KSOEvaluateMacro
+ // Only editing done is replacing #include "X" filepaths where necessary
+ // most of these args could be private static members of the class
+ private static bool ParseShaderFilesRecursive(List<ParsedShaderFile> filesParsed, string newTopLevelDirectory, string filePath, List<Macro> macros, Material material, Dictionary<string,bool> removeBetweenKeywords)
+ {
+ // Infinite recursion check
+ if (filesParsed.Exists(x => x.filePath == filePath)) return true;
+
+ ParsedShaderFile psf = new ParsedShaderFile();
+ psf.filePath = filePath;
+ filesParsed.Add(psf);
+
+ // Read file
+ string fileContents = null;
+ try
+ {
+ StreamReader sr = new StreamReader(filePath);
+ fileContents = sr.ReadToEnd();
+ sr.Close();
+ }
+ catch (FileNotFoundException e)
+ {
+ Debug.LogError("[Shader Optimizer] Shader file " + filePath + " not found. " + e.ToString());
+ return false;
+ }
+ catch (IOException e)
+ {
+ Debug.LogError("[Shader Optimizer] Error reading shader file. " + e.ToString());
+ return false;
+ }
+
+ // Parse file line by line
+ List<String> macrosList = new List<string>();
+ string[] fileLines = fileContents.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
+
+ List<string> includedLines = new List<string>();
+
+ bool isIncluded = true;
+ int isNotIncludedAtDepth = 0;
+ int ifStacking = 0;
+ Stack<bool> removeEndifStack = new Stack<bool>();
+
+ bool isCommentedOut = false;
+
+ int removedViaKeyword = 0;
+
+ for (int i=0; i<fileLines.Length; i++)
+ {
+ string lineParsed = fileLines[i].TrimStart();
+
+ //Remove stuff between comment keywords
+ string trimmedForKeyword = lineParsed.TrimStart('/').TrimEnd();
+ if (removeBetweenKeywords.ContainsKey(trimmedForKeyword))
+ {
+ removeBetweenKeywords[trimmedForKeyword] = !removeBetweenKeywords[trimmedForKeyword];
+ if (removeBetweenKeywords[trimmedForKeyword])
+ removedViaKeyword++;
+ else
+ removedViaKeyword--;
+ }
+ if (removedViaKeyword > 0) continue;
+
+ //removes empty lines
+ if (lineParsed.Length == 0) continue;
+ //removes code that is commented
+ if (lineParsed== "*/")
+ {
+ isCommentedOut = false;
+ continue;
+ }
+ else if (lineParsed == "/*")
+ {
+ isCommentedOut = true;
+ continue;
+ }
+ else if (lineParsed.StartsWith("//", StringComparison.Ordinal))
+ {
+ continue;
+ }
+ if (isCommentedOut) continue;
+
+ //Removed code from defines blocks
+ if (REMOVE_UNUSED_IF_DEFS)
+ {
+ //Check if Line contains #ifs
+ if (lineParsed.StartsWith("#if", StringComparison.Ordinal))
+ {
+ bool hasMultiple = lineParsed.Contains('&') || lineParsed.Contains('|');
+ if (!hasMultiple && lineParsed.StartsWith("#ifdef", StringComparison.Ordinal))
+ {
+ string keyword = lineParsed.Substring(6).Trim().Split(' ')[0];
+ bool allowRemoveal = (DontRemoveIfBranchesKeywords.Contains(keyword) == false) && KeywordsUsedByPragmas.Contains(keyword);
+ bool isRemoved = false;
+ if (isIncluded && allowRemoveal)
+ {
+ if ((material.IsKeywordEnabled(keyword) == false))
+ {
+ isIncluded = false;
+ isNotIncludedAtDepth = ifStacking;
+ isRemoved = true;
+ }
+ }
+ ifStacking++;
+ removeEndifStack.Push(isRemoved);
+ if (isRemoved) continue;
+ }
+ else if (!hasMultiple && lineParsed.StartsWith("#ifndef", StringComparison.Ordinal))
+ {
+ string keyword = lineParsed.Substring(7).Trim().Split(' ')[0];
+ bool allowRemoveal = DontRemoveIfBranchesKeywords.Contains(keyword) == false && KeywordsUsedByPragmas.Contains(keyword);
+ bool isRemoved = false;
+ if (isIncluded && allowRemoveal)
+ {
+ if (material.IsKeywordEnabled(keyword) == true)
+ {
+ isIncluded = false;
+ isNotIncludedAtDepth = ifStacking;
+ isRemoved = true;
+ }
+ }
+ ifStacking++;
+ removeEndifStack.Push(isRemoved);
+ if (isRemoved) continue;
+ }
+ else
+ {
+ ifStacking++;
+ removeEndifStack.Push(false);
+ }
+ }
+ else if (lineParsed.StartsWith("#else"))
+ {
+ if (isIncluded && removeEndifStack.Peek()) isIncluded = false;
+ if (!isIncluded && ifStacking - 1 == isNotIncludedAtDepth) isIncluded = true;
+ if (removeEndifStack.Peek()) continue;
+ }
+ else if (lineParsed.StartsWith("#endif", StringComparison.Ordinal))
+ {
+ ifStacking--;
+ if (ifStacking == isNotIncludedAtDepth)
+ {
+ isIncluded = true;
+ }
+ if (removeEndifStack.Pop()) continue;
+ }
+ if (!isIncluded) continue;
+ }
+
+ //Remove pragmas
+ if (lineParsed.StartsWith("#pragma shader_feature", StringComparison.Ordinal))
+ {
+ string keyword = lineParsed.Split(' ')[2];
+ if (KeywordsUsedByPragmas.Contains(keyword) == false) KeywordsUsedByPragmas.Add(keyword);
+ continue;
+ }
+
+ // Specifically requires no whitespace between # and include, as it should be
+ if (lineParsed.StartsWith("#include", StringComparison.Ordinal))
+ {
+ int firstQuotation = lineParsed.IndexOf('\"',0);
+ int lastQuotation = lineParsed.IndexOf('\"',firstQuotation+1);
+ string includeFilename = lineParsed.Substring(firstQuotation+1, lastQuotation-firstQuotation-1);
+
+ // Skip default includes
+ if (DefaultUnityShaderIncludes.Contains(includeFilename) == false)
+ {
+ string includeFullpath = includeFilename;
+ if (includeFilename.StartsWith("Assets/", StringComparison.Ordinal) == false)//not absolute
+ {
+ includeFullpath = GetFullPath(includeFilename, Path.GetDirectoryName(filePath));
+ }
+ if (!ParseShaderFilesRecursive(filesParsed, newTopLevelDirectory, includeFullpath, macros, material, removeBetweenKeywords))
+ return false;
+ //Change include to be be ralative to only one directory up, because all files are moved into the same folder
+ fileLines[i] = fileLines[i].Replace(includeFilename, "/"+includeFilename.Split('/').Last());
+ }
+ }
+ // Specifically requires no whitespace between // and KSOEvaluateMacro
+ else if (lineParsed == "//KSOEvaluateMacro")
+ {
+ string macro = "";
+ string lineTrimmed = null;
+ do
+ {
+ i++;
+ lineTrimmed = fileLines[i].TrimEnd();
+ if (lineTrimmed.EndsWith("\\", StringComparison.Ordinal))
+ macro += lineTrimmed.TrimEnd('\\') + Environment.NewLine; // keep new lines in macro to make output more readable
+ else macro += lineTrimmed;
+ }
+ while (lineTrimmed.EndsWith("\\", StringComparison.Ordinal));
+ macrosList.Add(macro);
+ }
+
+ includedLines.Add(fileLines[i]);
+ }
+
+ // Prepare the macros list into pattern matchable structs
+ // Revise this later to not do so many string ops
+ foreach (string macroString in macrosList)
+ {
+ string m = macroString;
+ Macro macro = new Macro();
+ m = m.TrimStart();
+ if (m[0] != '#') continue;
+ m = m.Remove(0, "#".Length).TrimStart();
+ if (!m.StartsWith("define", StringComparison.Ordinal)) continue;
+ m = m.Remove(0, "define".Length).TrimStart();
+ int firstParenthesis = m.IndexOf('(');
+ macro.name = m.Substring(0, firstParenthesis);
+ m = m.Remove(0, firstParenthesis + "(".Length);
+ int lastParenthesis = m.IndexOf(')');
+ string allArgs = m.Substring(0, lastParenthesis).Remove(' ').Remove('\t');
+ macro.args = allArgs.Split(',');
+ m = m.Remove(0, lastParenthesis + ")".Length);
+ macro.contents = m;
+ macros.Add(macro);
+ }
+
+ // Save psf lines to list
+ psf.lines = includedLines.ToArray();
+ return true;
+ }
+
+ // error CS1501: No overload for method 'Path.GetFullPath' takes 2 arguments
+ // Thanks Unity
+ // Could be made more efficent with stringbuilder
+ public static string GetFullPath(string relativePath, string basePath)
+ {
+ while (relativePath.StartsWith("./"))
+ relativePath = relativePath.Remove(0, "./".Length);
+ while (relativePath.StartsWith("../"))
+ {
+ basePath = basePath.Remove(basePath.LastIndexOf(Path.DirectorySeparatorChar), basePath.Length - basePath.LastIndexOf(Path.DirectorySeparatorChar));
+ relativePath = relativePath.Remove(0, "../".Length);
+ }
+ return basePath + '/' + relativePath;
+ }
+
+ // Replace properties! The meat of the shader optimization process
+ // For each constantProp, pattern match and find each instance of the property that isn't a declaration
+ // most of these args could be private static members of the class
+ private static void ReplaceShaderValues(Material material, string[] lines, int startLine, int endLine,
+ MaterialProperty[] props, List<PropertyData> constants, List<Macro> macros, List<GrabPassReplacement> grabPassVariables)
+ {
+ List <TextureProperty> uniqueSampledTextures = new List<TextureProperty>();
+
+ // Outside loop is each line
+ for (int i=startLine;i<endLine;i++)
+ {
+ string lineTrimmed = lines[i].TrimStart();
+ if (lineTrimmed.StartsWith("#pragma geometry", StringComparison.Ordinal))
+ {
+ if (!UseGeometry)
+ lines[i] = "//" + lines[i];
+ else
+ {
+ switch (CurrentLightmode)
+ {
+ case LightModeType.ForwardBase:
+ if (!UseGeometryForwardBase)
+ lines[i] = "//" + lines[i];
+ break;
+ case LightModeType.ForwardAdd:
+ if (!UseGeometryForwardAdd)
+ lines[i] = "//" + lines[i];
+ break;
+ case LightModeType.ShadowCaster:
+ if (!UseGeometryShadowCaster)
+ lines[i] = "//" + lines[i];
+ break;
+ case LightModeType.Meta:
+ if (!UseGeometryMeta)
+ lines[i] = "//" + lines[i];
+ break;
+ }
+ }
+ }
+ else if (lineTrimmed.StartsWith("#pragma hull", StringComparison.Ordinal) || lineTrimmed.StartsWith("#pragma domain", StringComparison.Ordinal))
+ {
+ if (!UseTessellation)
+ lines[i] = "//" + lines[i];
+ else
+ {
+ switch (CurrentLightmode)
+ {
+ case LightModeType.ForwardBase:
+ if (!UseTessellationForwardBase)
+ lines[i] = "//" + lines[i];
+ break;
+ case LightModeType.ForwardAdd:
+ if (!UseTessellationForwardAdd)
+ lines[i] = "//" + lines[i];
+ break;
+ case LightModeType.ShadowCaster:
+ if (!UseTessellationShadowCaster)
+ lines[i] = "//" + lines[i];
+ break;
+ case LightModeType.Meta:
+ if (!UseTessellationMeta)
+ lines[i] = "//" + lines[i];
+ break;
+ }
+ }
+ }
+ // Replace inline smapler states
+ else if (UseInlineSamplerStates && lineTrimmed.StartsWith("//KSOInlineSamplerState", StringComparison.Ordinal))
+ {
+ string lineParsed = lineTrimmed.Remove(' ').Remove('\t');
+ // Remove all whitespace
+ int firstParenthesis = lineParsed.IndexOf('(');
+ int lastParenthesis = lineParsed.IndexOf(')');
+ string argsString = lineParsed.Substring(firstParenthesis+1, lastParenthesis - firstParenthesis-1);
+ string[] args = argsString.Split(',');
+ MaterialProperty texProp = Array.Find(props, x => x.name == args[1]);
+ if (texProp != null)
+ {
+ Texture t = texProp.textureValue;
+ int inlineSamplerIndex = 0;
+ if (t != null)
+ {
+ switch (t.filterMode)
+ {
+ case FilterMode.Bilinear:
+ break;
+ case FilterMode.Point:
+ inlineSamplerIndex += 1 * 4;
+ break;
+ case FilterMode.Trilinear:
+ inlineSamplerIndex += 2 * 4;
+ break;
+ }
+ switch (t.wrapMode)
+ {
+ case TextureWrapMode.Repeat:
+ break;
+ case TextureWrapMode.Clamp:
+ inlineSamplerIndex += 1;
+ break;
+ case TextureWrapMode.Mirror:
+ inlineSamplerIndex += 2;
+ break;
+ case TextureWrapMode.MirrorOnce:
+ inlineSamplerIndex += 3;
+ break;
+ }
+ }
+
+ // Replace the token on the following line
+ lines[i+1] = lines[i+1].Replace(args[0], InlineSamplerStateNames[inlineSamplerIndex]);
+ }
+ }
+ else if (lineTrimmed.StartsWith("//KSODuplicateTextureCheckStart", StringComparison.Ordinal))
+ {
+ // Since files are not fully parsed and instead loosely processed, each shader function needs to have
+ // its sampled texture list reset somewhere before KSODuplicateTextureChecks are made.
+ // As long as textures are sampled in-order inside a single function, this method will work.
+ uniqueSampledTextures = new List<TextureProperty>();
+ }
+ else if (lineTrimmed.StartsWith("//KSODuplicateTextureCheck", StringComparison.Ordinal))
+ {
+ // Each KSODuplicateTextureCheck line gets evaluated when the shader is optimized
+ // If the texture given has already been sampled as another texture (i.e. one texture is used in two slots)
+ // AND has been sampled with the same UV mode - as indicated by a convention UV property,
+ // AND has been sampled with the exact same Tiling/Offset values
+ // AND has been logged by KSODuplicateTextureCheck,
+ // then the variable corresponding to the first instance of that texture being
+ // sampled will be assigned to the variable corresponding to the given texture.
+ // The compiler will then skip the duplicate texture sample since its variable is overwritten before being used
+
+ // Parse line for argument texture property name
+ string lineParsed = lineTrimmed.Replace(" ", "").Replace("\t", "");
+ int firstParenthesis = lineParsed.IndexOf('(');
+ int lastParenthesis = lineParsed.IndexOf(')');
+ string argName = lineParsed.Substring(firstParenthesis+1, lastParenthesis-firstParenthesis-1);
+ // Check if texture property by argument name exists and has a texture assigned
+ if (Array.Exists(props, x => x.name == argName))
+ {
+ MaterialProperty argProp = Array.Find(props, x => x.name == argName);
+ if (argProp.textureValue != null)
+ {
+ // If no convention UV property exists, sampled UV mode is assumed to be 0
+ // Any UV enum or mode indicator can be used for this
+ int UV = 0;
+ if (Array.Exists(props, x => x.name == argName + "UV"))
+ UV = (int)(Array.Find(props, x => x.name == argName + "UV").floatValue);
+
+ Vector2 texScale = material.GetTextureScale(argName);
+ Vector2 texOffset = material.GetTextureOffset(argName);
+
+ // Check if this texture has already been sampled
+ if (uniqueSampledTextures.Exists(x => (x.texture == argProp.textureValue)
+ && (x.uv == UV)
+ && (x.scale == texScale)
+ && x.offset == texOffset))
+ {
+ string texName = uniqueSampledTextures.Find(x => (x.texture == argProp.textureValue) && (x.uv == UV)).name;
+ // convention _var variables requried. i.e. _MainTex_var and _CoverageMap_var
+ lines[i] = argName + "_var = " + texName + "_var;";
+ }
+ else
+ {
+ // Texture/UV/ST combo hasn't been sampled yet, add it to the list
+ TextureProperty tp = new TextureProperty();
+ tp.name = argName;
+ tp.texture = argProp.textureValue;
+ tp.uv = UV;
+ tp.scale = texScale;
+ tp.offset = texOffset;
+ uniqueSampledTextures.Add(tp);
+ }
+ }
+ }
+ }
+ else if (lineTrimmed.StartsWith("[maxtessfactor(", StringComparison.Ordinal))
+ {
+ MaterialProperty maxTessFactorProperty = Array.Find(props, x => x.name == TessellationMaxFactorPropertyName);
+ if (maxTessFactorProperty != null)
+ {
+ float maxTessellation = maxTessFactorProperty.floatValue;
+ string animateTag = material.GetTag(TessellationMaxFactorPropertyName + AnimatedTagSuffix, false, "0");
+ if (animateTag != "" && animateTag == "1")
+ maxTessellation = 64.0f;
+ lines[i] = "[maxtessfactor(" + maxTessellation.ToString(".0######") + ")]";
+ }
+ }
+
+ // then replace macros
+ foreach (Macro macro in macros)
+ {
+ // Expects only one instance of a macro per line!
+ int macroIndex;
+ if ((macroIndex = lines[i].IndexOf(macro.name + "(", StringComparison.Ordinal)) != -1)
+ {
+ // Macro exists on this line, make sure its not the definition
+ string lineParsed = lineTrimmed.Remove(' ').Remove('\t');
+ if (lineParsed.StartsWith("#define", StringComparison.Ordinal)) continue;
+
+ // parse args between first '(' and first ')'
+ int firstParenthesis = macroIndex + macro.name.Length;
+ int lastParenthesis = lines[i].IndexOf(')', macroIndex + macro.name.Length+1);
+ string allArgs = lines[i].Substring(firstParenthesis+1, lastParenthesis-firstParenthesis-1);
+ string[] args = allArgs.Split(',');
+
+ // Replace macro parts
+ string newContents = macro.contents;
+ for (int j=0; j<args.Length;j++)
+ {
+ args[j] = args[j].Trim();
+ int argIndex;
+ int lastIndex = 0;
+ while ((argIndex = newContents.IndexOf(macro.args[j], lastIndex, StringComparison.Ordinal)) != -1)
+ {
+ lastIndex = argIndex+1;
+ char charLeft = ' ';
+ if (argIndex-1 >= 0)
+ charLeft = newContents[argIndex-1];
+ char charRight = ' ';
+ if (argIndex+macro.args[j].Length < newContents.Length)
+ charRight = newContents[argIndex+macro.args[j].Length];
+ if (ValidSeparators.Contains(charLeft) && ValidSeparators.Contains(charRight))
+ {
+ // Replcae the arg!
+ StringBuilder sbm = new StringBuilder(newContents.Length - macro.args[j].Length + args[j].Length);
+ sbm.Append(newContents, 0, argIndex);
+ sbm.Append(args[j]);
+ sbm.Append(newContents, argIndex + macro.args[j].Length, newContents.Length - argIndex - macro.args[j].Length);
+ newContents = sbm.ToString();
+ }
+ }
+ }
+ newContents = newContents.Replace("##", ""); // Remove token pasting separators
+ // Replace the line with the evaluated macro
+ StringBuilder sb = new StringBuilder(lines[i].Length + newContents.Length);
+ sb.Append(lines[i], 0, macroIndex);
+ sb.Append(newContents);
+ sb.Append(lines[i], lastParenthesis+1, lines[i].Length - lastParenthesis-1);
+ lines[i] = sb.ToString();
+ }
+ }
+ // then replace properties
+ foreach (PropertyData constant in constants)
+ {
+ int constantIndex;
+ int lastIndex = 0;
+ bool declarationFound = false;
+ while ((constantIndex = lines[i].IndexOf(constant.name, lastIndex, StringComparison.Ordinal)) != -1)
+ {
+ lastIndex = constantIndex+1;
+ char charLeft = ' ';
+ if (constantIndex-1 >= 0)
+ charLeft = lines[i][constantIndex-1];
+ char charRight = ' ';
+ if (constantIndex + constant.name.Length < lines[i].Length)
+ charRight = lines[i][constantIndex + constant.name.Length];
+ // Skip invalid matches (probably a subname of another symbol)
+ if (!(ValidSeparators.Contains(charLeft) && ValidSeparators.Contains(charRight)))
+ continue;
+
+ // Skip basic declarations of unity shader properties i.e. "uniform float4 _Color;"
+ if (!declarationFound)
+ {
+ string precedingText = lines[i].Substring(0, constantIndex-1).TrimEnd(); // whitespace removed string immediately to the left should be float or float4
+ string restOftheFile = lines[i].Substring(constantIndex + constant.name.Length).TrimStart(); // whitespace removed character immediately to the right should be ;
+ if (Array.Exists(ValidPropertyDataTypes, x => precedingText.EndsWith(x, StringComparison.Ordinal)) && restOftheFile.StartsWith(";", StringComparison.Ordinal))
+ {
+ declarationFound = true;
+ continue;
+ }
+ }
+
+ // Replace with constant!
+ // This could technically be more efficient by being outside the IndexOf loop
+ StringBuilder sb = new StringBuilder(lines[i].Length * 2);
+ sb.Append(lines[i], 0, constantIndex);
+ switch (constant.type)
+ {
+ case PropertyType.Float:
+ sb.Append("float(" + constant.value.x.ToString(CultureInfo.InvariantCulture) + ")");
+ break;
+ case PropertyType.Vector:
+ sb.Append("float4("+constant.value.x.ToString(CultureInfo.InvariantCulture)+","
+ +constant.value.y.ToString(CultureInfo.InvariantCulture)+","
+ +constant.value.z.ToString(CultureInfo.InvariantCulture)+","
+ +constant.value.w.ToString(CultureInfo.InvariantCulture)+")");
+ break;
+ }
+ sb.Append(lines[i], constantIndex+constant.name.Length, lines[i].Length-constantIndex-constant.name.Length);
+ lines[i] = sb.ToString();
+
+ // Check for Unity branches on previous line here?
+ }
+ }
+
+ // Then replace grabpass variable names
+ foreach (GrabPassReplacement gpr in grabPassVariables)
+ {
+ // find indexes of all instances of gpr.originalName that exist on this line
+ int lastIndex = 0;
+ int gbIndex;
+ while ((gbIndex = lines[i].IndexOf(gpr.originalName, lastIndex, StringComparison.Ordinal)) != -1)
+ {
+ lastIndex = gbIndex+1;
+ char charLeft = ' ';
+ if (gbIndex-1 >= 0)
+ charLeft = lines[i][gbIndex-1];
+ char charRight = ' ';
+ if (gbIndex + gpr.originalName.Length < lines[i].Length)
+ charRight = lines[i][gbIndex + gpr.originalName.Length];
+ // Skip invalid matches (probably a subname of another symbol)
+ if (!(ValidSeparators.Contains(charLeft) && ValidSeparators.Contains(charRight)))
+ continue;
+
+ // Replace with new variable name
+ // This could technically be more efficient by being outside the IndexOf loop
+ StringBuilder sb = new StringBuilder(lines[i].Length * 2);
+ sb.Append(lines[i], 0, gbIndex);
+ sb.Append(gpr.newName);
+ sb.Append(lines[i], gbIndex+gpr.originalName.Length, lines[i].Length-gbIndex-gpr.originalName.Length);
+ lines[i] = sb.ToString();
+ }
+ }
+
+ // Then remove Unity branches
+ if (RemoveUnityBranches)
+ lines[i] = lines[i].Replace("UNITY_BRANCH", "").Replace("[branch]", "");
+ }
+ }
+
+ public enum UnlockSuccess { hasNoSavedShader, wasNotLocked, couldNotFindOriginalShader, couldNotDeleteLockedShader,
+ success}
+ private static void Unlock(Material material, MaterialProperty shaderOptimizer = null)
+ {
+ //if unlock success set floats. not done for locking cause the sucess is checked later when applying the shaders
+ UnlockSuccess success = ShaderOptimizer.UnlockConcrete(material);
+ if (success == UnlockSuccess.success || success == UnlockSuccess.wasNotLocked
+ || success == UnlockSuccess.couldNotDeleteLockedShader)
+ {
+ if (shaderOptimizer != null) shaderOptimizer.floatValue = 0;
+ else material.SetFloat(GetOptimizerPropertyName(material.shader), 0);
+ }
+ }
+ private static UnlockSuccess UnlockConcrete (Material material)
+ {
+ Shader lockedShader = material.shader;
+ // Revert to original shader
+ string originalShaderName = material.GetTag("OriginalShader", false, "");
+ if (originalShaderName == "")
+ {
+ if (material.shader.name.StartsWith("Hidden/"))
+ {
+ Debug.LogError("[Shader Optimizer] Original shader not saved to material, could not unlock shader");
+ return UnlockSuccess.hasNoSavedShader;
+ }
+ else
+ {
+ Debug.LogWarning("[Shader Optimizer] Original shader not saved to material, but material also doesnt seem to be locked.");
+ return UnlockSuccess.wasNotLocked;
+ }
+
+ }
+ Shader orignalShader = Shader.Find(originalShaderName);
+ if (orignalShader == null)
+ {
+ if (material.shader.name.StartsWith("Hidden/"))
+ {
+ Debug.LogError("[Shader Optimizer] Original shader " + originalShaderName + " could not be found");
+ return UnlockSuccess.couldNotFindOriginalShader;
+ }
+ else
+ {
+ Debug.LogWarning("[Shader Optimizer] Original shader not saved to material, but material also doesnt seem to be locked.");
+ return UnlockSuccess.wasNotLocked;
+ }
+ }
+
+ // For some reason when shaders are swapped on a material the RenderType override tag gets completely deleted and render queue set back to -1
+ // So these are saved as temp values and reassigned after switching shaders
+ string renderType = material.GetTag("RenderType", false, "");
+ int renderQueue = material.renderQueue;
+ material.shader = orignalShader;
+ material.SetOverrideTag("RenderType", renderType);
+ material.renderQueue = renderQueue;
+
+ // Delete the variants folder and all files in it, as to not orhpan files and inflate Unity project
+
+ bool isOtherShaderUsingLockedShader = AssetDatabase.FindAssets("t:material").Select(g => AssetDatabase.LoadAssetAtPath<Material>(AssetDatabase.GUIDToAssetPath(g))).
+ Any(m => m.shader == lockedShader && m != material);
+ if (!isOtherShaderUsingLockedShader)
+ {
+ string materialFilePath = AssetDatabase.GetAssetPath(lockedShader);
+ string lockedFolder = Path.GetDirectoryName(materialFilePath);
+ FileUtil.DeleteFileOrDirectory(lockedFolder);
+ FileUtil.DeleteFileOrDirectory(lockedFolder + ".meta");
+ }
+ //AssetDatabase.Refresh();
+
+ return UnlockSuccess.success;
+ }
+
+ public static void DeleteTags(Material[] materials)
+ {
+ foreach(Material m in materials)
+ {
+ var it = new SerializedObject(m).GetIterator();
+ while (it.Next(true))
+ {
+ if (it.name == "stringTagMap")
+ {
+ for (int i = 0; i < it.arraySize; i++)
+ {
+ string tagName = it.GetArrayElementAtIndex(i).displayName;
+ if (tagName.EndsWith(AnimatedTagSuffix))
+ {
+ m.SetOverrideTag(tagName, "");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ #region Upgrade
+
+ public static void UpgradeAnimatedPropertiesToTagsOnAllMaterials()
+ {
+ IEnumerable<Material> materials = Resources.FindObjectsOfTypeAll<Material>();
+ UpgradeAnimatedPropertiesToTags(materials);
+ Debug.Log("[Thry][Optimizer] Update animated properties of all materials to tags.");
+ }
+
+ public static void UpgradeAnimatedPropertiesToTags(IEnumerable<Material> iMaterials)
+ {
+ IEnumerable<Material> materialsToChange = iMaterials.Where(m => m != null &&
+ string.IsNullOrEmpty(AssetDatabase.GetAssetPath(m)) == false && string.IsNullOrEmpty(AssetDatabase.GetAssetPath(m.shader)) == false
+ && IsShaderUsingThryOptimizer(m.shader)).Distinct().OrderBy(m => m.shader.name);
+
+ int i = 0;
+ foreach (Material m in materialsToChange)
+ {
+ if(EditorUtility.DisplayCancelableProgressBar("Upgrading Materials", "Upgrading animated tags of " + m.name, (float)i / materialsToChange.Count()))
+ {
+ break;
+ }
+
+ string path = AssetDatabase.GetAssetPath(m);
+ StreamReader reader = new StreamReader(path);
+ string line;
+ while((line = reader.ReadLine()) != null)
+ {
+ if (line.Contains(AnimatedPropertySuffix) && line.Length > 6)
+ {
+ string[] parts = line.Substring(6, line.Length - 6).Split(':');
+ float f;
+ if (float.TryParse(parts[1], out f))
+ {
+ if( f != 0)
+ {
+ string name = parts[0].Substring(0, parts[0].Length - AnimatedPropertySuffix.Length);
+ m.SetOverrideTag(name + AnimatedTagSuffix, "" + f);
+ }
+ }
+ }
+ }
+ reader.Close();
+ i++;
+ }
+
+ EditorUtility.ClearProgressBar();
+ }
+
+ static void ClearConsole()
+ {
+ var logEntries = System.Type.GetType("UnityEditor.LogEntries, UnityEditor.dll");
+
+ var clearMethod = logEntries.GetMethod("Clear", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
+
+ clearMethod.Invoke(null, null);
+ }
+
+ #endregion
+
+ //---GameObject + Children Locking
+
+ [MenuItem("GameObject/Thry/Materials/Unlock All", false,0)]
+ static void UnlockAllChildren()
+ {
+ SetLockForAllChildren(Selection.gameObjects, 0, true);
+ }
+
+ [MenuItem("GameObject/Thry/Materials/Lock All", false,0)]
+ static void LockAllChildren()
+ {
+ SetLockForAllChildren(Selection.gameObjects, 1, true);
+ }
+
+ //---Asset Unlocking
+
+ [MenuItem("Assets/Thry/Materials/Unlock All", false, 303)]
+ static void UnlockAllMaterials()
+ {
+ IEnumerable<Material> mats = Selection.assetGUIDs.Select(g => AssetDatabase.LoadAssetAtPath<Material>(AssetDatabase.GUIDToAssetPath(g)));
+ SetLockedForAllMaterials(mats, 0, true);
+ }
+
+ [MenuItem("Assets/Thry/Materials/Unlock All", true)]
+ static bool UnlockAllMaterialsValidator()
+ {
+ return SelectedObjectsAreLockableMaterials();
+ }
+
+ //---Asset Locking
+
+ [MenuItem("Assets/Thry/Materials/Lock All", false, 303)]
+ static void LockAllMaterials()
+ {
+ IEnumerable<Material> mats = Selection.assetGUIDs.Select(g => AssetDatabase.LoadAssetAtPath<Material>(AssetDatabase.GUIDToAssetPath(g)));
+ SetLockedForAllMaterials(mats, 1, true);
+ }
+
+ [MenuItem("Assets/Thry/Materials/Lock All", true)]
+ static bool LockAllMaterialsValidator()
+ {
+ return SelectedObjectsAreLockableMaterials();
+ }
+
+ //----Folder Lock
+
+ //This does not work for folders on the left side of the project explorer, because they are not exposed to Selection
+ [MenuItem("Assets/Thry/Materials/Lock Folder", false, 303)]
+ static void LockFolder()
+ {
+ IEnumerable<string> folderPaths = Selection.objects.Select(o => AssetDatabase.GetAssetPath(o)).Where(p => Directory.Exists(p));
+ List<Material> materials = new List<Material>();
+ foreach (string f in folderPaths) FindMaterialsRecursive(f, materials);
+ SetLockedForAllMaterials(materials, 1, true);
+ }
+
+ [MenuItem("Assets/Thry/Materials/Lock Folder", true)]
+ static bool LockFolderValidator()
+ {
+ return Selection.objects.Select(o => AssetDatabase.GetAssetPath(o)).Where(p => Directory.Exists(p)).Count() == Selection.objects.Length;
+ }
+
+ //-----Folder Unlock
+
+ [MenuItem("Assets/Thry/Materials/Unlock Folder", false, 303)]
+ static void UnLockFolder()
+ {
+ IEnumerable<string> folderPaths = Selection.objects.Select(o => AssetDatabase.GetAssetPath(o)).Where(p => Directory.Exists(p));
+ List<Material> materials = new List<Material>();
+ foreach (string f in folderPaths) FindMaterialsRecursive(f, materials);
+ SetLockedForAllMaterials(materials, 0, true);
+ }
+
+ [MenuItem("Assets/Thry/Materials/Unlock Folder", true)]
+ static bool UnLockFolderValidator()
+ {
+ return Selection.objects.Select(o => AssetDatabase.GetAssetPath(o)).Where(p => Directory.Exists(p)).Count() == Selection.objects.Length;
+ }
+
+ private static void FindMaterialsRecursive(string folderPath, List<Material> materials)
+ {
+ foreach(string f in Directory.GetFiles(folderPath))
+ {
+ if(AssetDatabase.GetMainAssetTypeAtPath(f) == typeof(Material))
+ {
+ materials.Add(AssetDatabase.LoadAssetAtPath<Material>(f));
+ }
+ }
+ foreach(string f in Directory.GetDirectories(folderPath)){
+ FindMaterialsRecursive(f, materials);
+ }
+ }
+
+ //----Folder Unlock
+
+ static bool SelectedObjectsAreLockableMaterials()
+ {
+ if (Selection.assetGUIDs != null && Selection.assetGUIDs.Length > 0)
+ {
+ return Selection.assetGUIDs.All(g =>
+ {
+ if (AssetDatabase.GetMainAssetTypeAtPath(AssetDatabase.GUIDToAssetPath(g)) != typeof(Material))
+ return false;
+ Material m = AssetDatabase.LoadAssetAtPath<Material>(AssetDatabase.GUIDToAssetPath(g));
+ return IsShaderUsingThryOptimizer(m.shader);
+ });
+ }
+ return false;
+ }
+
+ //----VRChat Callback to force Locking on upload
+
+#if VRC_SDK_VRCSDK2 || VRC_SDK_VRCSDK3
+ public class LockMaterialsOnUpload : IVRCSDKPreprocessAvatarCallback
+ {
+ public int callbackOrder => 100;
+
+ public bool OnPreprocessAvatar(GameObject avatarGameObject)
+ {
+ List<Material> materials = avatarGameObject.GetComponentsInChildren<Renderer>(true).SelectMany(r => r.sharedMaterials).ToList();
+#if VRC_SDK_VRCSDK3 && !UDON
+ VRCAvatarDescriptor descriptor = avatarGameObject.GetComponent<VRCAvatarDescriptor>();
+ if(descriptor != null)
+ {
+ IEnumerable<AnimationClip> clips = descriptor.baseAnimationLayers.Select(l => l.animatorController).Where(a => a != null).SelectMany(a => a.animationClips).Distinct();
+ foreach (AnimationClip clip in clips)
+ {
+ IEnumerable<Material> clipMaterials = AnimationUtility.GetObjectReferenceCurveBindings(clip).Where(b => b.isPPtrCurve && b.type.IsSubclassOf(typeof(Renderer)) && b.propertyName.StartsWith("m_Materials"))
+ .SelectMany(b => AnimationUtility.GetObjectReferenceCurve(clip, b)).Select(r => r.value as Material);
+ materials.AddRange(clipMaterials);
+ }
+ }
+
+#endif
+ SetLockedForAllMaterials(materials, 1, showProgressbar: true, showDialog: PersistentData.Get<bool>("ShowLockInDialog", true), allowCancel: false);
+ //returning true all the time, because build process cant be stopped it seems
+ return true;
+ }
+ }
+#endif
+
+#if VRC_SDK_VRCSDK2 || VRC_SDK_VRCSDK3
+ public class LockMaterialsOnWorldUpload : IVRCSDKBuildRequestedCallback
+ {
+ public int callbackOrder => 100;
+
+ bool IVRCSDKBuildRequestedCallback.OnBuildRequested(VRCSDKRequestedBuildType requestedBuildType)
+ {
+ List<Material> materials = new List<Material>();
+ if (requestedBuildType == VRCSDKRequestedBuildType.Scene)
+ {
+ if (UnityEngine.Object.FindObjectsOfType(typeof(VRC_SceneDescriptor)) is VRC_SceneDescriptor[] descriptors && descriptors.Length > 0){
+ var renderers = UnityEngine.Object.FindObjectsOfType<Renderer>();
+ foreach (var rend in renderers)
+ {
+ foreach (var mat in rend.sharedMaterials){
+ materials.Add(mat);
+ }
+ }
+ }
+ SetLockedForAllMaterials(materials, 1, showProgressbar: true, showDialog: PersistentData.Get<bool>("ShowLockInDialog", true), allowCancel: false);
+ }
+ return true;
+ }
+ }
+#endif
+
+ static string MaterialToShaderPropertyHash(Material m)
+ {
+ StringBuilder stringBuilder = new StringBuilder(m.shader.name);
+
+ foreach (MaterialProperty prop in
+ MaterialEditor.GetMaterialProperties(new Object[] { m }))
+ {
+ string propName = prop.name;
+
+ if (PropertiesToSkipInMaterialEquallityComparission.Contains(propName)) continue;
+
+ string isAnimated = GetAnimatedTag(m, propName);
+
+ if (isAnimated == "1")
+ {
+ stringBuilder.Append(isAnimated);
+ }
+ else if(isAnimated == "2")
+ {
+ //This is because materials with renaming should not share shaders
+ stringBuilder.Append(m.name);
+ }
+ else
+ {
+
+ switch (prop.type)
+ {
+ case MaterialProperty.PropType.Color:
+ stringBuilder.Append(m.GetColor(propName).ToString());
+ break;
+ case MaterialProperty.PropType.Vector:
+ stringBuilder.Append(m.GetVector(propName).ToString());
+ break;
+ case MaterialProperty.PropType.Range:
+ case MaterialProperty.PropType.Float:
+ stringBuilder.Append(m.GetFloat(propName)
+ .ToString(CultureInfo.InvariantCulture));
+ break;
+ case MaterialProperty.PropType.Texture:
+ Texture t = m.GetTexture(propName);
+ Vector4 texelSize = new Vector4(1.0f, 1.0f, 1.0f, 1.0f);
+ if (t != null)
+ texelSize = new Vector4(1.0f / t.width, 1.0f / t.height, t.width, t.height);
+
+ stringBuilder.Append(m.GetTextureOffset(propName).ToString());
+ stringBuilder.Append(m.GetTextureScale(propName).ToString());
+ break;
+ }
+ }
+ }
+
+ // https://forum.unity.com/threads/hash-function-for-game.452779/
+ ASCIIEncoding encoding = new ASCIIEncoding();
+ byte[] bytes = encoding.GetBytes(stringBuilder.ToString());
+ var sha = new MD5CryptoServiceProvider();
+ return BitConverter.ToString(sha.ComputeHash(bytes)).Replace("-", "").ToLower();
+ }
+
+ public static bool SetLockForAllChildren(GameObject[] objects, int lockState, bool showProgressbar = false, bool showDialog = false, bool allowCancel = true)
+ {
+ IEnumerable<Material> materials = objects.Select(o => o.GetComponentsInChildren<Renderer>(true)).SelectMany(rA => rA.SelectMany(r => r.sharedMaterials));
+ return SetLockedForAllMaterials(materials, lockState, showProgressbar, showDialog);
+ }
+
+ static Dictionary<string, Material> s_shaderPropertyCombinations = new Dictionary<string, Material>();
+ public static bool SetLockedForAllMaterials(IEnumerable<Material> materials, int lockState, bool showProgressbar = false, bool showDialog = false, bool allowCancel = true, MaterialProperty shaderOptimizer = null)
+ {
+ Helper.RegisterEditorUse();
+ //first the shaders are created. compiling is suppressed with start asset editing
+ AssetDatabase.StartAssetEditing();
+
+ //Get cleaned materia list
+ IEnumerable<Material> materialsToChangeLock = materials.Where(m => m != null &&
+ string.IsNullOrEmpty(AssetDatabase.GetAssetPath(m)) == false && string.IsNullOrEmpty(AssetDatabase.GetAssetPath(m.shader)) == false
+ && IsShaderUsingThryOptimizer(m.shader) && m.GetFloat(GetOptimizerPropertyName(m.shader)) != lockState).Distinct();
+
+ float i = 0;
+ float length = materialsToChangeLock.Count();
+
+ //show popup dialog if defined
+ if (showDialog && length > 0)
+ {
+ if(EditorUtility.DisplayDialog("Locking Materials", Locale.editor.Get("auto_lock_dialog").ReplaceVariables(length), "More information","OK"))
+ {
+ Application.OpenURL("https://www.youtube.com/watch?v=asWeDJb5LAo");
+ }
+ PersistentData.Set("ShowLockInDialog", false);
+ }
+
+ //Create shader assets
+ foreach (Material m in materialsToChangeLock.ToList()) //have to call ToList() here otherwise the Unlock Shader button in the ShaderGUI doesn't work
+ {
+ //do progress bar
+ if (showProgressbar)
+ {
+ if (allowCancel)
+ {
+ if (EditorUtility.DisplayCancelableProgressBar((lockState == 1) ? "Locking Materials" : "Unlocking Materials", m.name, i / length)) break;
+ }
+ else
+ {
+ EditorUtility.DisplayProgressBar((lockState == 1) ? "Locking Materials" : "Unlocking Materials", m.name, i / length);
+ }
+ }
+ //create the assets
+ try
+ {
+ if (lockState == 1)
+ {
+ string hash = MaterialToShaderPropertyHash(m);
+ // Check that shader has already been created for this hash and still exists
+ if (s_shaderPropertyCombinations.ContainsKey(hash) && Shader.Find(applyStructsLater[s_shaderPropertyCombinations[hash]].newShaderName) != null)
+ {
+ // Reuse existing shader and struct
+ ApplyStruct applyStruct = applyStructsLater[s_shaderPropertyCombinations[hash]];
+ applyStruct.material = m;
+ applyStructsLater[m] = applyStruct;
+ //Disable shader keywords
+ foreach (string keyword in m.shaderKeywords)
+ if (m.IsKeywordEnabled(keyword)) m.DisableKeyword(keyword);
+
+ }
+ // Create new locked shader
+ else
+ {
+ ShaderOptimizer.Lock(m,
+ MaterialEditor.GetMaterialProperties(new UnityEngine.Object[] { m }),
+ applyShaderLater: true);
+ s_shaderPropertyCombinations.Add(hash, m);
+ }
+ }
+ else if (lockState == 0)
+ {
+ ShaderOptimizer.Unlock(m, shaderOptimizer);
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.LogError("Could not un-/lock material " + m.name);
+ Debug.LogError(e);
+ }
+ i++;
+ }
+
+ EditorUtility.ClearProgressBar();
+ AssetDatabase.StopAssetEditing();
+ //unity now compiles all the shaders
+
+ //now all new shaders are applied. this has to happen after unity compiled the shaders
+ if (lockState == 1)
+ {
+ //Apply new shaders
+ foreach (Material m in materialsToChangeLock)
+ {
+ if (ShaderOptimizer.LockApplyShader(m))
+ {
+ m.SetFloat(GetOptimizerPropertyName(m.shader), 1);
+ }
+ }
+ if(ShaderEditor.Active != null && ShaderEditor.Active.IsDrawing)
+ {
+ GUIUtility.ExitGUI();
+ }
+ }
+ AssetDatabase.Refresh();
+ return true;
+ }
+
+ public static string GetOptimizerPropertyName(Shader shader)
+ {
+ if (isShaderUsingThryOptimizer.ContainsKey(shader))
+ {
+ if (isShaderUsingThryOptimizer[shader] == false) return null;
+ return shaderThryOptimizerPropertyName[shader];
+ }
+ else
+ {
+ if (IsShaderUsingThryOptimizer(shader) == false) return null;
+ return shaderThryOptimizerPropertyName[shader];
+ }
+ }
+
+ private static Dictionary<Shader, string> shaderThryOptimizerPropertyName = new Dictionary<Shader, string>();
+ private static Dictionary<Shader, bool> isShaderUsingThryOptimizer = new Dictionary<Shader, bool>();
+ public static bool IsShaderUsingThryOptimizer(Shader shader)
+ {
+ if (isShaderUsingThryOptimizer.ContainsKey(shader))
+ {
+ return isShaderUsingThryOptimizer[shader];
+ }
+ SerializedObject shaderObject = new SerializedObject(shader);
+ SerializedProperty props = shaderObject.FindProperty("m_ParsedForm.m_PropInfo.m_Props");
+ if (props != null)
+ {
+ foreach (SerializedProperty p in props)
+ {
+ SerializedProperty at = p.FindPropertyRelative("m_Attributes");
+ if (at.arraySize > 0)
+ {
+ if (at.GetArrayElementAtIndex(0).stringValue == "ThryShaderOptimizerLockButton")
+ {
+ //Debug.Log(shader.name + " found to use optimizer ");
+ isShaderUsingThryOptimizer[shader] = true;
+ shaderThryOptimizerPropertyName[shader] = p.displayName;
+ return true;
+ }
+ }
+ }
+ }
+ isShaderUsingThryOptimizer[shader] = false;
+ return false;
+ }
+
+ public static bool IsMaterialLocked(Material material)
+ {
+ return material.shader.name.StartsWith("Hidden/") && material.GetTag("OriginalShader", false, "") != "";
+ }
+
+ private static Dictionary<Shader, int> shaderUsedTextureReferencesCount = new Dictionary<Shader, int>();
+ public static int GetUsedTextureReferencesCount(Shader s)
+ {
+ //Shader.m_ParsedForm.m_SubShaders[i].m_Passes[j].m_Programs[k].m_SubPrograms[l].m_Parameters[m].m_TextureParams[n]
+ //m_Programs not avaiable in unity 2019
+ return 0;
+ /*if (shaderUsedTextureReferencesCount.ContainsKey(s)) return shaderUsedTextureReferencesCount[s];
+ SerializedObject shaderObject = new SerializedObject(s);
+ SerializedProperty m_SubShaders = shaderObject.FindProperty("m_ParsedForm.m_SubShaders");
+ for (int i_subShader = 0; i_subShader < m_SubShaders.arraySize; i_subShader++)
+ {
+ SerializedProperty m_Passes = m_SubShaders.GetArrayElementAtIndex(i_subShader).FindPropertyRelative("m_Passes");
+ for (int i_passes = 0; i_passes < m_Passes.arraySize; i_passes++)
+ {
+ SerializedProperty m_Programs = m_Passes.GetArrayElementAtIndex(i_passes);
+ foreach (SerializedProperty p in m_Programs) Debug.Log(p.displayName);
+ }
+ }
+ return 0;*/
+ }
+ }
+
+ public class UnlockedMaterialsList : EditorWindow
+ {
+
+ static Dictionary<Shader, List<Material>> unlockedMaterialsByShader = new Dictionary<Shader, List<Material>>();
+ private void OnEnable()
+ {
+ UpdateList();
+ }
+
+ void UpdateList()
+ {
+ unlockedMaterialsByShader.Clear();
+ List<Material> unlockedMaterials = new List<Material>();
+ string[] guids = AssetDatabase.FindAssets("t:material");
+ float step = 1.0f / guids.Length;
+ float f = 0;
+ EditorUtility.DisplayProgressBar("Searching materials...", "", f);
+ foreach (string g in guids)
+ {
+ Material m = AssetDatabase.LoadAssetAtPath<Material>(AssetDatabase.GUIDToAssetPath(g));
+ if (m != null && m.shader != null && ShaderOptimizer.IsShaderUsingThryOptimizer(m.shader) && ShaderOptimizer.IsMaterialLocked(m) == false)
+ {
+ unlockedMaterials.Add(m);
+ }
+ f = f + step;
+ EditorUtility.DisplayProgressBar("Searching materials...", m.name, f);
+ }
+ foreach (IGrouping<Shader, Material> materials in unlockedMaterials.GroupBy(m => m.shader))
+ {
+ unlockedMaterialsByShader.Add(materials.Key, materials.ToList());
+ }
+ EditorUtility.ClearProgressBar();
+ }
+
+ private void OnGUI()
+ {
+ EditorGUILayout.LabelField("Unlocked Materials", Styles.EDITOR_LABEL_HEADER);
+ if (GUILayout.Button("Update List")) UpdateList();
+ if (unlockedMaterialsByShader.Count == 0)
+ {
+ GUILayout.Label("All your materials are locked.", Styles.greenStyle);
+ }
+ foreach (KeyValuePair<Shader, List<Material>> shaderMaterials in unlockedMaterialsByShader)
+ {
+ EditorGUILayout.Space();
+
+ EditorGUILayout.LabelField(shaderMaterials.Key.name);
+ List<Material> lockedMaterials = new List<Material>();
+ foreach (Material m in shaderMaterials.Value)
+ {
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.ObjectField(m, typeof(Material), false);
+ //EditorGUILayout.IntField(ShaderOptimizer.GetUsedTextureReferencesCount(m.shader));
+ if (GUILayout.Button("Lock"))
+ {
+ ShaderOptimizer.SetLockedForAllMaterials(new List<Material>() { m }, 1, true, false, true);
+ lockedMaterials.Add(m);
+ }
+ EditorGUILayout.EndHorizontal();
+ }
+
+ foreach (Material m in lockedMaterials)
+ shaderMaterials.Value.Remove(m);
+ }
+ EditorGUILayout.Space();
+ if (GUILayout.Button("Lock All"))
+ {
+ ShaderOptimizer.SetLockedForAllMaterials(unlockedMaterialsByShader.Values.SelectMany(col => col), 1, true, false, true);
+ UpdateList();
+ }
+ }
+
+ }
+}
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ShaderOptimizer.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ShaderOptimizer.cs.meta
new file mode 100644
index 00000000..21f57306
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ShaderOptimizer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 01a32216cdf0b0e44af9eb3d82fff780
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ShaderTranslator.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ShaderTranslator.cs
new file mode 100644
index 00000000..931b35f0
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ShaderTranslator.cs
@@ -0,0 +1,238 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using UnityEditor;
+using UnityEngine;
+using static Thry.ThryEditor.ShaderTranslator;
+
+namespace Thry.ThryEditor
+{
+ public class ShaderTranslator : ScriptableObject
+ {
+ public string Name;
+ public string OriginShader;
+ public string TargetShader;
+ public bool MatchOriginShaderBasedOnRegex;
+ public bool MatchTargetShaderBasedOnRegex;
+ public string OriginShaderRegex;
+ public string TargetShaderRegex;
+ public List<PropertyTranslation> PropertyTranslations;
+
+ [Serializable]
+ public class PropertyTranslation
+ {
+ public string Origin;
+ public string Target;
+ public string Math;
+ }
+
+ public void Apply(ShaderEditor editor)
+ {
+ Shader originShader = Shader.Find(OriginShader);
+ Shader targetShader = Shader.Find(TargetShader);
+ SerializedObject serializedMaterial = new SerializedObject(editor.Materials[0]);
+
+ foreach(PropertyTranslation trans in PropertyTranslations)
+ {
+ if (editor.PropertyDictionary.ContainsKey(trans.Target))
+ {
+ SerializedProperty p;
+ switch (editor.PropertyDictionary[trans.Target].MaterialProperty.type)
+ {
+ case MaterialProperty.PropType.Float:
+ case MaterialProperty.PropType.Range:
+ p = GetProperty(serializedMaterial, "m_SavedProperties.m_Floats", trans.Origin);
+ if (p != null)
+ {
+ float f = p.FindPropertyRelative("second").floatValue;
+ if (trans.Math.Length > 0) f = Helper.SolveMath(trans.Math, f);
+ editor.PropertyDictionary[trans.Target].MaterialProperty.floatValue = f;
+ }
+ break;
+ case MaterialProperty.PropType.Vector:
+ p = GetProperty(serializedMaterial, "m_SavedProperties.m_Colors", trans.Origin);
+ if (p != null) editor.PropertyDictionary[trans.Target].MaterialProperty.vectorValue = p.FindPropertyRelative("second").vector4Value;
+ break;
+ case MaterialProperty.PropType.Color:
+ p = GetProperty(serializedMaterial, "m_SavedProperties.m_Colors", trans.Origin);
+ if (p != null) editor.PropertyDictionary[trans.Target].MaterialProperty.colorValue = p.FindPropertyRelative("second").colorValue;
+ break;
+ case MaterialProperty.PropType.Texture:
+ p = GetProperty(serializedMaterial, "m_SavedProperties.m_TexEnvs", trans.Origin);
+ if (p != null)
+ {
+ SerializedProperty values = p.FindPropertyRelative("second");
+ editor.PropertyDictionary[trans.Target].MaterialProperty.textureValue =
+ values.FindPropertyRelative("m_Texture").objectReferenceValue as Texture;
+ Vector2 scale = values.FindPropertyRelative("m_Scale").vector2Value;
+ Vector2 offset = values.FindPropertyRelative("m_Offset").vector2Value;
+ editor.PropertyDictionary[trans.Target].MaterialProperty.textureScaleAndOffset =
+ new Vector4(scale.x, scale.y , offset.x, offset.y);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ SerializedProperty GetProperty(SerializedObject o, string arrayPath, string propertyName)
+ {
+ SerializedProperty array = o.FindProperty(arrayPath);
+ for(int i = 0; i < array.arraySize; i++)
+ {
+ if (array.GetArrayElementAtIndex(i).displayName == propertyName)
+ {
+ return array.GetArrayElementAtIndex(i);
+ }
+ }
+ return null;
+ }
+
+ static List<ShaderTranslator> s_translationDefinitions;
+ static List<ShaderTranslator> TranslationDefinitions
+ {
+ get
+ {
+ if (s_translationDefinitions == null)
+ s_translationDefinitions = AssetDatabase.FindAssets("t:" + nameof(ShaderTranslator)).Select(
+ g => AssetDatabase.LoadAssetAtPath<ShaderTranslator>(AssetDatabase.GUIDToAssetPath(g))).ToList();
+ return s_translationDefinitions;
+ }
+ }
+
+ public static ShaderTranslator CheckForExistingTranslationFile(Shader origin, Shader target)
+ {
+ return TranslationDefinitions.FirstOrDefault(t =>
+ t.MatchOriginShaderBasedOnRegex ? (Regex.IsMatch(origin.name, t.OriginShaderRegex)) : (t.OriginShader == origin.name) &&
+ t.MatchTargetShaderBasedOnRegex ? (Regex.IsMatch(target.name, t.TargetShaderRegex)) : (t.TargetShader == target.name) );
+ }
+
+ public static void SuggestedTranslationButtonGUI(ShaderEditor editor)
+ {
+ if(editor.SuggestedTranslationDefinition != null)
+ {
+ if(GUILayout.Button("Apply " + editor.SuggestedTranslationDefinition.Name + " shader translation."))
+ {
+ editor.SuggestedTranslationDefinition.Apply(editor);
+ }
+ }
+ }
+
+ public static void TranslationSelectionGUI(ShaderEditor editor)
+ {
+ Rect r;
+ if (GuiHelper.ButtonWithCursor(Styles.icon_style_shaders, "Shader Translation", 25, 25, out r))
+ {
+ EditorUtility.DisplayCustomMenu(r, TranslationDefinitions.Select(t => new GUIContent(t.Name)).ToArray(), -1, ConfirmTranslationSelection, editor);
+ }
+ }
+
+ static void ConfirmTranslationSelection(object userData, string[] options, int selected)
+ {
+ TranslationDefinitions[selected].Apply(userData as ShaderEditor);
+ }
+
+ [MenuItem("Assets/Thry/ShaderTranslator/New Definition", false, 380)]
+ public static void CreateNewTranslationDefinition()
+ {
+ ShaderTranslator shaderTranslator = CreateInstance<ShaderTranslator>();
+ string path = UnityHelper.GetCurrentAssetExplorerFolder() + "/shaderTranslationDefinition.asset";
+ AssetDatabase.CreateAsset(shaderTranslator, path);
+ EditorGUIUtility.PingObject(shaderTranslator);
+ TranslationDefinitions.Add(shaderTranslator);
+ }
+ }
+
+ public class ShaderTranslatorSelecUI : EditorWindow
+ {
+
+ }
+
+ [CustomEditor(typeof(ShaderTranslator))]
+ public class ShaderTranslatorEditorUI : Editor
+ {
+ public override void OnInspectorGUI()
+ {
+ serializedObject.Update();
+ ShaderTranslator translator = serializedObject.targetObject as ShaderTranslator;
+
+ translator.Name = EditorGUILayout.TextField("Translation File Name: " , translator.Name);
+
+ GUILayout.Space(10);
+
+ string[] shaders = AssetDatabase.FindAssets("t:shader").Select(g => AssetDatabase.LoadAssetAtPath<Shader>(AssetDatabase.GUIDToAssetPath(g)).name).
+ Where(s => s.StartsWith("Hidden") == false).ToArray();
+
+ EditorGUI.BeginChangeCheck();
+ int originIndex = EditorGUILayout.Popup("From Shader", Array.IndexOf(shaders, translator.OriginShader), shaders);
+ if (EditorGUI.EndChangeCheck()) translator.OriginShader = shaders[originIndex];
+
+ EditorGUI.BeginChangeCheck();
+ int targetIndex = EditorGUILayout.Popup("To Shader", Array.IndexOf(shaders, translator.TargetShader), shaders);
+ if (EditorGUI.EndChangeCheck()) translator.TargetShader = shaders[targetIndex];
+
+ translator.MatchOriginShaderBasedOnRegex = EditorGUILayout.ToggleLeft(new GUIContent("Match Origin Shader Using Regex",
+ "Match the origin shader for suggestions based on a regex definition."), translator.MatchOriginShaderBasedOnRegex);
+ if (translator.MatchOriginShaderBasedOnRegex)
+ translator.OriginShaderRegex = EditorGUILayout.TextField("Origin Shader Regex", translator.OriginShaderRegex);
+ translator.MatchTargetShaderBasedOnRegex = EditorGUILayout.ToggleLeft(new GUIContent("Match Target Shader Using Regex",
+ "Match the target shader for suggestions based on a regex definition."), translator.MatchTargetShaderBasedOnRegex);
+ if (translator.MatchTargetShaderBasedOnRegex)
+ translator.TargetShaderRegex = EditorGUILayout.TextField("Target Shader Regex", translator.TargetShaderRegex);
+
+ if (originIndex < 0 || targetIndex < 0)
+ {
+ EditorGUILayout.HelpBox("Could not find origin or target shader.", MessageType.Error);
+ return;
+ }
+
+ Shader origin = Shader.Find(shaders[originIndex]);
+ Shader target = Shader.Find(shaders[targetIndex]);
+
+ GUILayout.Space(10);
+
+ using (new GUILayout.VerticalScope("box"))
+ {
+ GUILayout.Label("Property Translation", EditorStyles.boldLabel);
+ GUILayout.BeginHorizontal();
+ GUILayout.Label("From");
+ GUILayout.Label("To");
+ GUILayout.Label("Math");
+ GUILayout.EndHorizontal();
+ List<PropertyTranslation> remove = new List<PropertyTranslation>();
+ foreach (PropertyTranslation trans in translator.PropertyTranslations)
+ {
+ Rect fullWidth = EditorGUILayout.GetControlRect();
+ Rect r = fullWidth;
+ r.width = (r.width - 20) / 3;
+ if (GUI.Button(r, trans.Origin)) GuiHelper.SearchableEnumPopup.CreateSearchableEnumPopup(
+ MaterialEditor.GetMaterialProperties(new UnityEngine.Object[] { new Material(origin) }).Select(p => p.name).ToArray(), trans.Origin,
+ (newValue) => trans.Origin = newValue);
+ r.x += r.width;
+ if (GUI.Button(r, trans.Target)) GuiHelper.SearchableEnumPopup.CreateSearchableEnumPopup(
+ MaterialEditor.GetMaterialProperties(new UnityEngine.Object[] { new Material(target) }).Select(p => p.name).ToArray(), trans.Target,
+ (newValue) => trans.Target = newValue);
+ r.x += r.width;
+ trans.Math = EditorGUI.TextField(r, trans.Math);
+ r.x += r.width;
+ r.width = 20;
+ if (GUI.Button(r, GUIContent.none, Styles.icon_style_remove)) remove.Add(trans);
+ }
+
+ foreach (PropertyTranslation r in remove)
+ translator.PropertyTranslations.Remove(r);
+
+ Rect buttonRect = EditorGUILayout.GetControlRect();
+ buttonRect.x = buttonRect.width - 20;
+ buttonRect.width = 20;
+ if (GUI.Button(buttonRect, GUIContent.none, Styles.icon_style_add)) translator.PropertyTranslations.Add(new PropertyTranslation());
+ }
+
+ serializedObject.Update();
+ EditorUtility.SetDirty(serializedObject.targetObject);
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ShaderTranslator.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ShaderTranslator.cs.meta
new file mode 100644
index 00000000..aa73dae4
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ShaderTranslator.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 495e309f3fc473c4f846ca40e2993420
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Styles.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Styles.cs
new file mode 100644
index 00000000..053d92cd
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Styles.cs
@@ -0,0 +1,92 @@
+// Material/Shader Inspector for Unity 2017/2018
+// Copyright (C) 2019 Thryrallo
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEditor;
+using UnityEngine;
+
+namespace Thry
+{
+ public class Styles
+ {
+ public static GUIStyle masterLabel { get; private set; } = new GUIStyle(GUI.skin.label) { richText = true, alignment = TextAnchor.MiddleCenter };
+ public static GUIStyle EDITOR_LABEL_HEADER { get; private set; } = new GUIStyle(GUI.skin.label) { fontSize = 16, alignment = TextAnchor.MiddleCenter };
+ public static GUIStyle dropDownHeader { get; private set; } = new GUIStyle(new GUIStyle("ShurikenModuleTitle"))
+ {
+ font = new GUIStyle(EditorStyles.label).font,
+ fontSize = GUI.skin.font.fontSize,
+ border = new RectOffset(15, 7, 4, 4),
+ fixedHeight = 22,
+ contentOffset = new Vector2(20f, -2f)
+ };
+
+ public static Color COLOR_BG { get; private set; } = (EditorGUIUtility.isProSkin) ? new Color(0.4f, 0.4f, 0.4f) : new Color(0.8f, 0.8f, 0.8f);
+ public static Color COLOR_FG { get; private set; } = (EditorGUIUtility.isProSkin) ? new Color(0.8f, 0.8f, 0.8f) : Color.black;
+
+ private static Color COLOR_ICON_FONT = GUI.skin.label.normal.textColor;
+ private static Color COLOR_ICON_GRAY = EditorGUIUtility.isProSkin ? COLOR_ICON_FONT : new Color(0.4f, 0.4f, 0.4f);
+ public static Color COLOR_ICON_ACTIVE_CYAN = Color.cyan;
+ private static Color COLOR_ICON_ACTIVE_RED = Color.red;
+ public static Color COLOR_BACKGROUND_1 = EditorGUIUtility.isProSkin ? new Color(0.27f, 0.27f, 0.27f) : new Color(0.65f, 0.65f, 0.65f);
+ public static Color COLOR_BACKGROUND_2 = EditorGUIUtility.isProSkin ? new Color(0.5f, 0.5f, 0.5f) : new Color(0.85f, 0.85f, 0.85f);
+
+ public static GUIStyle dropDownHeaderLabel { get; private set; } = new GUIStyle(EditorStyles.boldLabel) { alignment = TextAnchor.MiddleCenter };
+ public static GUIStyle dropDownHeaderButton { get; private set; } = new GUIStyle(EditorStyles.toolbarButton);
+ public static GUIStyle bigTextureStyle { get; private set; } = new GUIStyle() { fontSize = 48 };
+ public static GUIStyle vectorPropertyStyle { get; private set; } = new GUIStyle() { padding = new RectOffset(0, 0, 2, 2) };
+ public static GUIStyle greenStyle { get; private set; } = new GUIStyle() { normal = new GUIStyleState() { textColor = new Color(0, 0.5f, 0) } };
+ public static GUIStyle animatedIndicatorStyle { get; private set; } = new GUIStyle() { normal = new GUIStyleState() { textColor = new Color(0.3f, 1, 0.3f) }, alignment = TextAnchor.MiddleRight };
+ public static GUIStyle orangeStyle { get; private set; } = new GUIStyle() { normal = new GUIStyleState() { textColor = new Color(0.9f, 0.5f, 0) } };
+ public static GUIStyle cyanStyle { get; private set; } = new GUIStyle() { normal = new GUIStyleState() { textColor = COLOR_ICON_ACTIVE_CYAN } };
+ public static GUIStyle redStyle { get; private set; } = new GUIStyle() { normal = new GUIStyleState() { textColor = Color.red } };
+ public static GUIStyle made_by_style { get; private set; } = new GUIStyle(EditorStyles.label) { fontSize = 10 };
+ public static GUIStyle notification_style { get; private set; } = new GUIStyle(GUI.skin.box) { fontSize = 12, wordWrap = true, normal = new GUIStyleState() { textColor = Color.red } };
+
+ public static GUIStyle style_toggle_left_richtext { get; private set; } = new GUIStyle(EditorStyles.label) { richText = true };
+
+ public static GUIStyle icon_style_help = CreateIconStyle(EditorGUIUtility.IconContent("_Help"));
+ public static GUIStyle icon_style_menu = CreateIconStyle(EditorGUIUtility.IconContent("_Menu"));
+ public static GUIStyle icon_style_settings = CreateIconStyle(EditorGUIUtility.IconContent("_Popup"));
+ public static GUIStyle icon_style_search = CreateIconStyle(EditorGUIUtility.IconContent("Search Icon"));
+ public static GUIStyle icon_style_presets = CreateIconStyle(EditorGUIUtility.IconContent("Preset.Context"));
+ public static GUIStyle icon_style_add = CreateIconStyle(EditorGUIUtility.IconContent("PrefabOverlayAdded Icon"));
+ public static GUIStyle icon_style_remove = CreateIconStyle(EditorGUIUtility.IconContent("PrefabOverlayRemoved Icon"));
+ public static GUIStyle icon_style_refresh = CreateIconStyle(EditorGUIUtility.IconContent("d_Refresh"));
+ public static GUIStyle icon_style_shaders = CreateIconStyle(EditorGUIUtility.IconContent("d_ShaderVariantCollection Icon"));
+ public static GUIStyle icon_style_linked = CreateIconStyle(LoadTextureByFileName(RESOURCE_NAME.ICON_NAME_LINK));
+ public static GUIStyle icon_style_thryIcon = CreateIconStyle(LoadTextureByFileName(RESOURCE_NAME.ICON_NAME_THRY));
+
+ public static Texture texture_icon_shaders = EditorGUIUtility.IconContent("d_ShaderVariantCollection Icon").image;
+
+ static GUIStyle CreateIconStyle(GUIContent content)
+ {
+ return CreateIconStyle(content.image as Texture2D);
+ }
+ static GUIStyle CreateIconStyle(Texture2D texture)
+ {
+ return new GUIStyle()
+ {
+ stretchWidth = true,
+ stretchHeight = true,
+ fixedHeight = 0,
+ fixedWidth = 0,
+ normal = new GUIStyleState()
+ {
+ background = texture
+ }
+ };
+ }
+
+ public static Texture2D rounded_texture { get; private set; } = LoadTextureByFileName(RESOURCE_NAME.RECT);
+
+ private static Texture2D LoadTextureByFileName(string search_name)
+ {
+ string[] guids = AssetDatabase.FindAssets(search_name + " t:texture");
+ if (guids.Length == 0)
+ return Texture2D.whiteTexture;
+ return AssetDatabase.LoadAssetAtPath<Texture2D>(AssetDatabase.GUIDToAssetPath(guids[0]));
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Styles.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Styles.cs.meta
new file mode 100644
index 00000000..2e0f5e87
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Styles.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0b3a6712cb451dc4fa105ca3acd64957
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryAssemblyDefinition.asmdef b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryAssemblyDefinition.asmdef
new file mode 100644
index 00000000..d7e6e1f4
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryAssemblyDefinition.asmdef
@@ -0,0 +1,14 @@
+{
+ "name": "ThryAssemblyDefinition",
+ "references": [],
+ "optionalUnityReferences": [],
+ "includePlatforms": [
+ "Editor"
+ ],
+ "excludePlatforms": [],
+ "allowUnsafeCode": false,
+ "overrideReferences": false,
+ "precompiledReferences": [],
+ "autoReferenced": true,
+ "defineConstraints": []
+} \ No newline at end of file
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryAssemblyDefinition.asmdef.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryAssemblyDefinition.asmdef.meta
new file mode 100644
index 00000000..f0646d57
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryAssemblyDefinition.asmdef.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 1efbd8ef56fbc0146a62083b5af6074c
+AssemblyDefinitionImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryEditor.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryEditor.cs
new file mode 100644
index 00000000..594b8e65
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryEditor.cs
@@ -0,0 +1,726 @@
+// Material/Shader Inspector for Unity 2017/2018
+// Copyright (C) 2019 Thryrallo
+
+using System.Collections.Generic;
+using System.IO;
+using System.Text.RegularExpressions;
+using UnityEditor;
+using UnityEngine;
+using Thry;
+using System;
+using System.Reflection;
+using System.Linq;
+using Thry.ThryEditor;
+
+namespace Thry
+{
+ public class ShaderEditor : ShaderGUI
+ {
+ public const string EXTRA_OPTIONS_PREFIX = "--";
+ public const float MATERIAL_NOT_RESET = 69.12f;
+
+ public const string PROPERTY_NAME_MASTER_LABEL = "shader_master_label";
+ public const string PROPERTY_NAME_LABEL_FILE = "shader_properties_label_file";
+ public const string PROPERTY_NAME_LOCALE = "shader_properties_locale";
+ public const string PROPERTY_NAME_ON_SWAP_TO_ACTIONS = "shader_on_swap_to";
+ public const string PROPERTY_NAME_SHADER_VERSION = "shader_version";
+ public const string PROPERTY_NAME_EDITOR_DETECT = "shader_is_using_thry_editor";
+
+ //Static
+ private static string s_edtiorDirectoryPath;
+
+ public static InputEvent Input = new InputEvent();
+ public static ShaderEditor Active;
+
+ // Stores the different shader properties
+ public ShaderGroup mainGroup;
+
+ // UI Instance Variables
+
+ public bool DoShowSearchBar;
+ private string _enteredSearchTerm = "";
+ private string _appliedSearchTerm = "";
+
+ // shader specified values
+ private ShaderHeaderProperty _shaderHeader = null;
+ private List<FooterButton> _footers;
+
+ // sates
+ private bool _isFirstOnGUICall = true;
+ private bool _wasUsed = false;
+ private bool _doReloadNextDraw = false;
+ private bool _didSwapToShader = false;
+
+ //EditorData
+ public MaterialEditor Editor;
+ public MaterialProperty[] Properties;
+ public Material[] Materials;
+ public Shader Shader;
+ public ShaderPart CurrentProperty;
+ public Dictionary<string, ShaderProperty> PropertyDictionary;
+ public List<ShaderPart> ShaderParts;
+ public List<ShaderProperty> TextureArrayProperties;
+ public bool IsFirstCall;
+ public bool DoUseShaderOptimizer;
+ public bool IsLockedMaterial;
+ public bool IsInAnimationMode;
+ public Renderer ActiveRenderer;
+ public string RenamedPropertySuffix;
+ public Locale Locale;
+ public ShaderTranslator SuggestedTranslationDefinition;
+
+ //Shader Versioning
+ private Version _shaderVersionLocal;
+ private Version _shaderVersionRemote;
+ private bool _hasShaderUpdateUrl = false;
+ private bool _isShaderUpToDate = true;
+ private string _shaderUpdateUrl = null;
+
+ //other
+ ShaderProperty ShaderOptimizerProperty { get; set; }
+
+ private DefineableAction[] _onSwapToActions = null;
+
+ public bool IsDrawing { get; private set; } = false;
+ public bool IsPresetEditor { get; private set; } = false;
+
+ //-------------Init functions--------------------
+
+ private Dictionary<string, string> LoadDisplayNamesFromFile()
+ {
+ //load display names from file if it exists
+ MaterialProperty label_file_property = GetMaterialProperty(PROPERTY_NAME_LABEL_FILE);
+ Dictionary<string, string> labels = new Dictionary<string, string>();
+ if (label_file_property != null)
+ {
+ string[] guids = AssetDatabase.FindAssets(label_file_property.displayName);
+ if (guids.Length == 0)
+ {
+ Debug.LogWarning("Label File could not be found");
+ return labels;
+ }
+ string path = AssetDatabase.GUIDToAssetPath(guids[0]);
+ string[] data = Regex.Split(Thry.FileHelper.ReadFileIntoString(path), @"\r?\n");
+ foreach (string d in data)
+ {
+ string[] set = Regex.Split(d, ":=");
+ if (set.Length > 1) labels[set[0]] = set[1];
+ }
+ }
+ return labels;
+ }
+
+ private PropertyOptions ExtractExtraOptionsFromDisplayName(ref string displayName)
+ {
+ if (displayName.Contains(EXTRA_OPTIONS_PREFIX))
+ {
+ string[] parts = displayName.Split(new string[] { EXTRA_OPTIONS_PREFIX }, 2, System.StringSplitOptions.None);
+ displayName = parts[0];
+ parts[1] = parts[1].Replace("''", "\"");
+ PropertyOptions options = Parser.ParseToObject<PropertyOptions>(parts[1]);
+ if (options != null)
+ {
+ if (options.condition_showS != null)
+ {
+ options.condition_show = DefineableCondition.Parse(options.condition_showS);
+ }
+ if (options.on_value != null)
+ {
+ options.on_value_actions = PropertyValueAction.ParseToArray(options.on_value);
+ }
+ return options;
+ }
+ }
+ return new PropertyOptions();
+ }
+
+ private enum ThryPropertyType
+ {
+ none, property, master_label, footer, header, headerWithEnd, legacy_header, legacy_header_end, legacy_header_start, group_start, group_end, instancing, dsgi, lightmap_flags, locale, on_swap_to, space, shader_version
+ }
+
+ private ThryPropertyType GetPropertyType(MaterialProperty p, PropertyOptions options)
+ {
+ string name = p.name;
+ MaterialProperty.PropFlags flags = p.flags;
+
+ if (DrawingData.LastPropertyDrawerType == DrawerType.Header)
+ return (DrawingData.LastPropertyDrawer as ThryHeaderDrawer).GetEndProperty() != null ? ThryPropertyType.headerWithEnd : ThryPropertyType.header;
+
+ if (flags == MaterialProperty.PropFlags.HideInInspector)
+ {
+ if (name == PROPERTY_NAME_MASTER_LABEL)
+ return ThryPropertyType.master_label;
+ if (name == PROPERTY_NAME_ON_SWAP_TO_ACTIONS)
+ return ThryPropertyType.on_swap_to;
+ if (name == PROPERTY_NAME_SHADER_VERSION)
+ return ThryPropertyType.shader_version;
+
+ if (name.StartsWith("m_start", StringComparison.Ordinal))
+ return ThryPropertyType.legacy_header_start;
+ if (name.StartsWith("m_end", StringComparison.Ordinal))
+ return ThryPropertyType.legacy_header_end;
+ if (name.StartsWith("m_", StringComparison.Ordinal))
+ return ThryPropertyType.legacy_header;
+ if (name.StartsWith("g_start", StringComparison.Ordinal))
+ return ThryPropertyType.group_start;
+ if (name.StartsWith("g_end", StringComparison.Ordinal))
+ return ThryPropertyType.group_end;
+ if (name.StartsWith("footer_", StringComparison.Ordinal))
+ return ThryPropertyType.footer;
+ if (name == "Instancing")
+ return ThryPropertyType.instancing;
+ if (name == "DSGI")
+ return ThryPropertyType.dsgi;
+ if (name == "LightmapFlags")
+ return ThryPropertyType.lightmap_flags;
+ if (name == PROPERTY_NAME_LOCALE)
+ return ThryPropertyType.locale;
+ if (name.StartsWith("space"))
+ return ThryPropertyType.space;
+ }
+ else if(flags.HasFlag(MaterialProperty.PropFlags.HideInInspector) == false)
+ {
+ if (!options.hide_in_inspector)
+ return ThryPropertyType.property;
+ }
+ return ThryPropertyType.none;
+ }
+
+ private void LoadLocales()
+ {
+ MaterialProperty locales_property = GetMaterialProperty(PROPERTY_NAME_LOCALE);
+ Locale = null;
+ if (locales_property != null)
+ {
+ string displayName = locales_property.displayName;
+ PropertyOptions options = ExtractExtraOptionsFromDisplayName(ref displayName);
+ Locale = new Locale(options.file_name);
+ Locale.selected_locale_index = (int)locales_property.floatValue;
+ }
+ }
+
+ //finds all properties and headers and stores them in correct order
+ private void CollectAllProperties()
+ {
+ //load display names from file if it exists
+ MaterialProperty[] props = Properties;
+ Dictionary<string, string> labels = LoadDisplayNamesFromFile();
+ LoadLocales();
+
+ PropertyDictionary = new Dictionary<string, ShaderProperty>();
+ ShaderParts = new List<ShaderPart>();
+ mainGroup = new ShaderGroup(this); //init top object that all Shader Objects are childs of
+ Stack<ShaderGroup> headerStack = new Stack<ShaderGroup>(); //header stack. used to keep track if editorData header to parent new objects to
+ headerStack.Push(mainGroup); //add top object as top object to stack
+ headerStack.Push(mainGroup); //add top object a second time, because it get's popped with first actual header item
+ _footers = new List<FooterButton>(); //init footer list
+ int headerCount = 0;
+
+ for (int i = 0; i < props.Length; i++)
+ {
+ string displayName = props[i].displayName;
+
+ //Load from label file
+ if (labels.ContainsKey(props[i].name)) displayName = labels[props[i].name];
+
+ //Check for locale
+ if (Locale != null)
+ {
+ if (displayName.StartsWith("locale::", StringComparison.Ordinal))
+ {
+ if (Locale.Constains(displayName))
+ {
+ displayName = Locale.Get(displayName);
+ }
+ }
+ }
+ //extract json data from display name
+ PropertyOptions options = ExtractExtraOptionsFromDisplayName(ref displayName);
+
+ int offset = options.offset + headerCount;
+
+ DrawingData.ResetLastDrawerData();
+ Editor.GetPropertyHeight(props[i]);
+
+ ThryPropertyType type = GetPropertyType(props[i], options);
+ switch (type)
+ {
+ case ThryPropertyType.header:
+ headerStack.Pop();
+ break;
+ case ThryPropertyType.legacy_header:
+ headerStack.Pop();
+ break;
+ case ThryPropertyType.headerWithEnd:
+ case ThryPropertyType.legacy_header_start:
+ offset = options.offset + ++headerCount;
+ break;
+ case ThryPropertyType.legacy_header_end:
+ headerStack.Pop();
+ headerCount--;
+ break;
+ case ThryPropertyType.on_swap_to:
+ _onSwapToActions = options.actions;
+ break;
+ }
+ ShaderProperty NewProperty = null;
+ ShaderPart newPart = null;
+ switch (type)
+ {
+ case ThryPropertyType.master_label:
+ _shaderHeader = new ShaderHeaderProperty(this, props[i], displayName, 0, options, false);
+ break;
+ case ThryPropertyType.footer:
+ _footers.Add(new FooterButton(Parser.ParseToObject<ButtonData>(displayName)));
+ break;
+ case ThryPropertyType.header:
+ case ThryPropertyType.headerWithEnd:
+ case ThryPropertyType.legacy_header:
+ case ThryPropertyType.legacy_header_start:
+ ShaderHeader newHeader = new ShaderHeader(this, props[i], Editor, displayName, offset, options);
+ headerStack.Peek().addPart(newHeader);
+ headerStack.Push(newHeader);
+ newPart = newHeader;
+ break;
+ case ThryPropertyType.group_start:
+ ShaderGroup new_group = new ShaderGroup(this, options);
+ headerStack.Peek().addPart(new_group);
+ headerStack.Push(new_group);
+ newPart = new_group;
+ break;
+ case ThryPropertyType.group_end:
+ headerStack.Pop();
+ break;
+ case ThryPropertyType.none:
+ case ThryPropertyType.property:
+ if (props[i].type == MaterialProperty.PropType.Texture)
+ NewProperty = new TextureProperty(this, props[i], displayName, offset, options, props[i].flags.HasFlag(MaterialProperty.PropFlags.NoScaleOffset) == false, !DrawingData.LastPropertyUsedCustomDrawer, i);
+ else
+ NewProperty = new ShaderProperty(this, props[i], displayName, offset, options, false, i);
+ break;
+ case ThryPropertyType.lightmap_flags:
+ NewProperty = new GIProperty(this, props[i], displayName, offset, options, false);
+ break;
+ case ThryPropertyType.dsgi:
+ NewProperty = new DSGIProperty(this, props[i], displayName, offset, options, false);
+ break;
+ case ThryPropertyType.instancing:
+ NewProperty = new InstancingProperty(this, props[i], displayName, offset, options, false);
+ break;
+ case ThryPropertyType.locale:
+ NewProperty = new LocaleProperty(this, props[i], displayName, offset, options, false);
+ break;
+ case ThryPropertyType.shader_version:
+ _shaderVersionRemote = new Version(WebHelper.GetCachedString(options.remote_version_url));
+ _shaderVersionLocal = new Version(displayName);
+ _isShaderUpToDate = _shaderVersionLocal >= _shaderVersionRemote;
+ _shaderUpdateUrl = options.generic_string;
+ _hasShaderUpdateUrl = _shaderUpdateUrl != null;
+ break;
+ }
+ if (NewProperty != null)
+ {
+ newPart = NewProperty;
+ if (PropertyDictionary.ContainsKey(props[i].name))
+ continue;
+ PropertyDictionary.Add(props[i].name, NewProperty);
+ if (type != ThryPropertyType.none)
+ headerStack.Peek().addPart(NewProperty);
+ }
+ //if new header is at end property
+ if (headerStack.Peek() is ShaderHeader && (headerStack.Peek() as ShaderHeader).GetEndProperty() == props[i].name)
+ {
+ headerStack.Pop();
+ headerCount--;
+ }
+ if (newPart != null)
+ {
+ ShaderParts.Add(newPart);
+ }
+ }
+ }
+
+
+ // Not in use cause getPropertyHandlerMethod is really expensive
+ private void HandleKeyworDrawers()
+ {
+ foreach (MaterialProperty p in Properties)
+ {
+ ShaderHelper.EnableDisableKeywordsBasedOnTheirFloatValue(Materials, Shader, p.name);
+ }
+ }
+
+
+ //-------------Draw Functions----------------
+
+ public void InitlizeThryUI()
+ {
+ Config config = Config.Singleton;
+ Active = this;
+ Helper.RegisterEditorUse();
+
+ //get material targets
+ Materials = Editor.targets.Select(o => o as Material).ToArray();
+
+ Shader = Materials[0].shader;
+
+ RenamedPropertySuffix = ShaderOptimizer.GetRenamedPropertySuffix(Materials[0]);
+
+ IsPresetEditor = Materials.Length == 1 && Presets.ArePreset(Materials);
+
+ //collect shader properties
+ CollectAllProperties();
+
+ if (ShaderOptimizer.IsShaderUsingThryOptimizer(Shader))
+ {
+ ShaderOptimizerProperty = PropertyDictionary[ShaderOptimizer.GetOptimizerPropertyName(Shader)];
+ if(ShaderOptimizerProperty != null) ShaderOptimizerProperty.ExemptFromLockedDisabling = true;
+ }
+
+ AddResetProperty();
+
+ _isFirstOnGUICall = false;
+ }
+
+ private Dictionary<string, MaterialProperty> materialPropertyDictionary;
+ public MaterialProperty GetMaterialProperty(string name)
+ {
+ if (materialPropertyDictionary == null)
+ {
+ materialPropertyDictionary = new Dictionary<string, MaterialProperty>();
+ foreach (MaterialProperty p in Properties)
+ if (materialPropertyDictionary.ContainsKey(p.name) == false) materialPropertyDictionary.Add(p.name, p);
+ }
+ if (materialPropertyDictionary.ContainsKey(name))
+ return materialPropertyDictionary[name];
+ return null;
+ }
+
+ private void AddResetProperty()
+ {
+ if (Materials[0].HasProperty(PROPERTY_NAME_EDITOR_DETECT) == false)
+ {
+ EditorChanger.AddThryProperty(Materials[0].shader);
+ }
+ Materials[0].SetFloat(PROPERTY_NAME_EDITOR_DETECT, 69);
+ }
+
+ public override void OnClosed(Material material)
+ {
+ base.OnClosed(material);
+ _isFirstOnGUICall = true;
+ }
+
+ public override void AssignNewShaderToMaterial(Material material, Shader oldShader, Shader newShader)
+ {
+ //Unity sets the render queue to the shader defult when changing shader
+ //This seems to be some deeper process that cant be disabled so i just set it again after the swap
+ //Even material.shader = newShader resets the queue. (this is actually the only thing the base function does)
+ int previousQueue = material.renderQueue;
+ base.AssignNewShaderToMaterial(material, oldShader, newShader);
+ material.renderQueue = previousQueue;
+ SuggestedTranslationDefinition = ShaderTranslator.CheckForExistingTranslationFile(oldShader, newShader);
+ _doReloadNextDraw = true;
+ _didSwapToShader = true;
+ }
+
+ void InitEditorData(MaterialEditor materialEditor)
+ {
+ Editor = materialEditor;
+ TextureArrayProperties = new List<ShaderProperty>();
+ IsFirstCall = true;
+ }
+
+ public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] props)
+ {
+ IsDrawing = true;
+ //Init
+ bool reloadUI = _isFirstOnGUICall || (_doReloadNextDraw && Event.current.type == EventType.Layout) || (materialEditor.target as Material).shader != Shader;
+ if (reloadUI)
+ {
+ InitEditorData(materialEditor);
+ Properties = props;
+ InitlizeThryUI();
+ }
+
+ //Update Data
+ Properties = props;
+ Shader = Materials[0].shader;
+ Input.Update(IsLockedMaterial);
+ ActiveRenderer = Selection.activeTransform?.GetComponent<Renderer>();
+ IsInAnimationMode = AnimationMode.InAnimationMode();
+
+ Active = this;
+
+ GUIManualReloadButton();
+ GUIShaderVersioning();
+
+ GUITopBar();
+ GUISearchBar();
+ Presets.PresetEditorGUI(this);
+ ShaderTranslator.SuggestedTranslationButtonGUI(this);
+
+ //PROPERTIES
+ foreach (ShaderPart part in mainGroup.parts)
+ {
+ part.Draw();
+ }
+
+ //Render Queue selection
+ if(VRCInterface.IsVRCSDKInstalled()) GuiHelper.VRCFallbackSelector(this);
+ if (Config.Singleton.showRenderQueue) materialEditor.RenderQueueField();
+
+ BetterTooltips.DrawActive();
+
+ GUIFooters();
+
+ HandleEvents();
+
+ IsDrawing = false;
+ }
+
+ private void GUIManualReloadButton()
+ {
+ if (Config.Singleton.showManualReloadButton)
+ {
+ if(GUILayout.Button("Manual Reload"))
+ {
+ this.Reload();
+ }
+ }
+ }
+
+ private void GUIShaderVersioning()
+ {
+ if (!_isShaderUpToDate)
+ {
+ Rect r = EditorGUILayout.GetControlRect(false, _hasShaderUpdateUrl ? 30 : 15);
+ EditorGUI.LabelField(r, $"[New Shader Version available] {_shaderVersionLocal} -> {_shaderVersionRemote}" + (_hasShaderUpdateUrl ? "\n Click here to download." : ""), Styles.redStyle);
+ if(Input.HadMouseDownRepaint && _hasShaderUpdateUrl && GUILayoutUtility.GetLastRect().Contains(Input.mouse_position)) Application.OpenURL(_shaderUpdateUrl);
+ }
+ }
+
+ private void GUITopBar()
+ {
+ //if header is texture, draw it first so other ui elements can be positions below
+ if (_shaderHeader != null && _shaderHeader.Options.texture != null) _shaderHeader.Draw();
+
+ bool drawAboveToolbar = EditorGUIUtility.wideMode == false;
+ if(drawAboveToolbar) _shaderHeader.Draw(new CRect(EditorGUILayout.GetControlRect()));
+
+ Rect mainHeaderRect = EditorGUILayout.BeginHorizontal();
+ //draw editor settings button
+ if (GuiHelper.ButtonWithCursor(Styles.icon_style_settings, "Settings", 25, 25))
+ {
+ EditorWindow.GetWindow<Settings>(false, "Thry Settings", true);
+ }
+ if (GuiHelper.ButtonWithCursor(Styles.icon_style_search, "Search", 25, 25))
+ {
+ DoShowSearchBar = !DoShowSearchBar;
+ if(!DoShowSearchBar) ClearSearch();
+ }
+ Presets.PresetGUI(this);
+
+ //draw master label text after ui elements, so it can be positioned between
+ if (_shaderHeader != null && !drawAboveToolbar) _shaderHeader.Draw(new CRect(mainHeaderRect));
+
+ GUILayout.FlexibleSpace();
+ ShaderTranslator.TranslationSelectionGUI(this);
+ if (GuiHelper.ButtonWithCursor(Styles.icon_style_thryIcon, "Thryrallo", 25, 25))
+ Application.OpenURL("https://www.twitter.com/thryrallo");
+ EditorGUILayout.EndHorizontal();
+ }
+
+ private void GUISearchBar()
+ {
+ if (DoShowSearchBar)
+ {
+ EditorGUI.BeginChangeCheck();
+ _enteredSearchTerm = EditorGUILayout.TextField(_enteredSearchTerm);
+ if (EditorGUI.EndChangeCheck())
+ {
+ _appliedSearchTerm = _enteredSearchTerm.ToLower();
+ UpdateSearch(mainGroup);
+ }
+ }
+ }
+
+ private void GUIFooters()
+ {
+ try
+ {
+ FooterButton.DrawList(_footers);
+ }
+ catch (Exception ex)
+ {
+ Debug.LogWarning(ex);
+ }
+ if (GUILayout.Button("@UI Made by Thryrallo", Styles.made_by_style))
+ Application.OpenURL("https://www.twitter.com/thryrallo");
+ EditorGUIUtility.AddCursorRect(GUILayoutUtility.GetLastRect(), MouseCursor.Link);
+ }
+
+ private void HandleEvents()
+ {
+ Event e = Event.current;
+ //if reloaded, set reload to false
+ if (_doReloadNextDraw && Event.current.type == EventType.Layout) _doReloadNextDraw = false;
+
+ //if was undo, reload
+ bool isUndo = (e.type == EventType.ExecuteCommand || e.type == EventType.ValidateCommand) && e.commandName == "UndoRedoPerformed";
+ if (isUndo) _doReloadNextDraw = true;
+
+
+ //on swap
+ if (_onSwapToActions != null && _didSwapToShader)
+ {
+ foreach (DefineableAction a in _onSwapToActions)
+ a.Perform();
+ _onSwapToActions = null;
+ _didSwapToShader = false;
+ }
+
+ //test if material has been reset
+ if (_wasUsed && e.type == EventType.Repaint)
+ {
+ if (Materials[0].HasProperty("shader_is_using_thry_editor") && Materials[0].GetFloat("shader_is_using_thry_editor") != 69)
+ {
+ _doReloadNextDraw = true;
+ HandleReset();
+ _wasUsed = true;
+ }
+ }
+
+ if (e.type == EventType.Used) _wasUsed = true;
+ if (Input.HadMouseDownRepaint) Input.HadMouseDown = false;
+ Input.HadMouseDownRepaint = false;
+ IsFirstCall = false;
+ materialPropertyDictionary = null;
+ }
+
+ //iterate the same way drawing would iterate
+ //if display part, display all parents parts
+ private void UpdateSearch(ShaderPart part)
+ {
+ part.has_not_searchedFor = part.Content.text.ToLower().Contains(_appliedSearchTerm) == false;
+ if (part is ShaderGroup)
+ {
+ foreach (ShaderPart p in (part as ShaderGroup).parts)
+ {
+ UpdateSearch(p);
+ part.has_not_searchedFor &= p.has_not_searchedFor;
+ }
+ }
+ }
+
+ private void ClearSearch()
+ {
+ _appliedSearchTerm = "";
+ UpdateSearch(mainGroup);
+ }
+
+ private void HandleReset()
+ {
+ MaterialLinker.UnlinkAll(Materials[0]);
+ ShaderOptimizer.DeleteTags(Materials);
+ }
+
+ public void Repaint()
+ {
+ if (Materials.Length > 0)
+ EditorUtility.SetDirty(Materials[0]);
+ }
+
+ public static void RepaintActive()
+ {
+ if (ShaderEditor.Active != null)
+ Active.Repaint();
+ }
+
+ public void Reload()
+ {
+ this._isFirstOnGUICall = true;
+ this._didSwapToShader = true;
+ this._doReloadNextDraw = true;
+ this.Repaint();
+ }
+
+ public static void ReloadActive()
+ {
+ if (ShaderEditor.Active != null)
+ Active.Reload();
+ }
+
+ public void ApplyDrawers()
+ {
+ foreach (Material target in Materials)
+ MaterialEditor.ApplyMaterialPropertyDrawers(target);
+ }
+
+ public static string GetShaderEditorDirectoryPath()
+ {
+ if (s_edtiorDirectoryPath == null)
+ {
+ IEnumerable<string> paths = AssetDatabase.FindAssets("ThryEditor").Select(g => AssetDatabase.GUIDToAssetPath(g));
+ foreach (string p in paths)
+ {
+ if (p.EndsWith("/ThryEditor.cs"))
+ s_edtiorDirectoryPath = Directory.GetParent(Path.GetDirectoryName(p)).FullName;
+ }
+ }
+ return s_edtiorDirectoryPath;
+ }
+
+ [MenuItem("Thry/Shader Tools/Fix Keywords (Very Slow)", priority = -20)]
+ static void FixKeywords()
+ {
+ IEnumerable<Material> materials = AssetDatabase.FindAssets("t:material").Select(g => AssetDatabase.GUIDToAssetPath(g)).Where(p => string.IsNullOrEmpty(p) == false)
+ .Select(p => AssetDatabase.LoadAssetAtPath<Material>(p)).Where(m => m != null && m.shader != null)
+ .Where(m => ShaderOptimizer.IsMaterialLocked(m) == false && ShaderHelper.IsShaderUsingThryShaderEditor(m.shader));
+ float f = 0;
+ int count = materials.Count();
+ foreach(Material m in materials)
+ {
+ for(int i= 0;i< m.shader.GetPropertyCount(); i++){
+ if (m.shader.GetPropertyType(i) == UnityEngine.Rendering.ShaderPropertyType.Float)
+ {
+ ShaderHelper.EnableDisableKeywordsBasedOnTheirFloatValue(new Material[] { m }, m.shader, m.shader.GetPropertyName(i));
+ }
+ }
+ EditorUtility.DisplayProgressBar("Fixing Keywords", m.name, f++ / count);
+ }
+ EditorUtility.ClearProgressBar();
+ }
+
+ [MenuItem("Thry/Twitter", priority = -100)]
+ static void MenuThryTwitter()
+ {
+ Application.OpenURL("https://www.twitter.com/thryrallo");
+ }
+
+ [MenuItem("Thry/ShaderUI/Settings",priority = -20)]
+ static void MenuShaderUISettings()
+ {
+ EditorWindow.GetWindow<Settings>(false, "Thry Settings", true);
+ }
+
+ [MenuItem("Thry/Shader Optimizer/Upgraded Animated Properties", priority = -20)]
+ static void MenuUpgradeAnimatedPropertiesToTagsOnAllMaterials()
+ {
+ ShaderOptimizer.UpgradeAnimatedPropertiesToTagsOnAllMaterials();
+ }
+
+ [MenuItem("Thry/ShaderUI/Use Thry Editor for other shaders", priority = 0)]
+ static void MenuShaderUIAddToShaders()
+ {
+ EditorWindow.GetWindow<EditorChanger>(false, "UI Changer", true);
+ }
+
+ [MenuItem("Thry/Shader Optimizer/Unlocked Materials List", priority = 0)]
+ static void MenuShaderOptUnlockedMaterials()
+ {
+ EditorWindow.GetWindow<UnlockedMaterialsList>(false, "Unlocked Materials", true);
+ }
+ }
+}
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryEditor.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryEditor.cs.meta
new file mode 100644
index 00000000..2379167a
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryEditor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d0ba26704202cee44b1927fe3b60da4e
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryFileBuilder.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryFileBuilder.cs
new file mode 100644
index 00000000..62cdad81
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryFileBuilder.cs
@@ -0,0 +1,65 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+
+namespace Thry {
+ public class ThryFileCreator {
+
+ [MenuItem("Thry/ShaderUI/UI Creator Helper/Create Label Boiler", false, priority = 40)]
+ public static void CreateLabel()
+ {
+ string[] names = GetProperties();
+ string data = names.Aggregate("", (n1, n2) => n1 + n2 + ":=" + n2 + "--{tooltip:}\n");
+ Save(data, "_label.txt");
+ }
+ [MenuItem("Thry/ShaderUI/UI Creator Helper/Create Label Boiler", true, priority = 40)]
+ static bool CreateLabelVaildate()
+ {
+ return ValidateSelection();
+ }
+
+ [MenuItem("Thry/ShaderUI/UI Creator Helper/Create Label Boiler + Locale Boiler", false, priority = 40)]
+ public static void CreateLabelLocale()
+ {
+ string[] names = GetProperties();
+ string label_data = names.Aggregate("", (n1, n2) => n1 +
+ n2 + ":=locale::" + n2 + "_text--{tooltip:locale::" + n2 + "_tooltip}\n");
+ string locale_data = names.Aggregate(",English\n", (n1, n2) => n1 +
+ n2 + "_text," + n2 + "\n"+
+ n2 + "_tooltip,\n");
+ Save(label_data, "_label.txt");
+ Save(locale_data, "_locale.txt");
+ }
+ [MenuItem("Thry/ShaderUI Creator Helper/Create Label Boiler + Locale Boiler", true, priority = 40)]
+ static bool CreateLabelLocaleValidate()
+ {
+ return ValidateSelection();
+ }
+
+ private static bool ValidateSelection()
+ {
+ if (Selection.activeObject == null)
+ return false;
+ string path = AssetDatabase.GetAssetPath(Selection.activeObject).ToLower();
+ return path.EndsWith(".shader");
+ }
+
+ private static string[] GetProperties()
+ {
+ Shader shader = (Shader)Selection.activeObject;
+ return MaterialEditor.GetMaterialProperties(new Material[] { new Material(shader) }).Select(p => p.name).ToArray();
+ }
+
+ private static void Save(string data, string add_string)
+ {
+ string path = AssetDatabase.GetAssetPath(Selection.activeObject);
+ path = Path.GetDirectoryName(path)+ "/"+ Path.GetFileNameWithoutExtension(path) + add_string;
+ FileHelper.WriteStringToFile(data, path);
+ AssetDatabase.Refresh();
+ EditorGUIUtility.PingObject(AssetDatabase.LoadMainAssetAtPath(path));
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryFileBuilder.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryFileBuilder.cs.meta
new file mode 100644
index 00000000..5dbf66ac
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryFileBuilder.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e2d2cf739465a3b49aad32cfd86f0f83
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryTexturePacker.compute b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryTexturePacker.compute
new file mode 100644
index 00000000..deaa52b0
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryTexturePacker.compute
@@ -0,0 +1,77 @@
+#pragma kernel CSMain
+
+RWTexture2D<float4> Result;
+
+float Width;
+float Height;
+bool TakeRGBFromRTexture;
+
+//Config: has texture, billinearFiltering / fallback, channel, invert
+
+Texture2D<float4> R_Input;
+float4 R_Config;
+
+Texture2D<float4> G_Input;
+float4 G_Config;
+
+Texture2D<float4> B_Input;
+float4 B_Config;
+
+Texture2D<float4> A_Input;
+float4 A_Config;
+
+SamplerState linearClampSampler;
+SamplerState pointClampSampler;
+
+float SampleInput(Texture2D<float4> tex, float4 config, float2 uv) {
+ if (config.r == 0) {
+ return config.g;
+ }
+ float value = 0;
+ float4 pixelColor = float4(1,1,1,1);
+ if(config.g == 0) pixelColor = tex.SampleLevel(pointClampSampler, uv, 0);
+ else pixelColor = pixelColor = tex.SampleLevel(linearClampSampler, uv, 0);
+ if (config.b == 0) value = pixelColor.r;
+ else if (config.b == 1) value = pixelColor.g;
+ else if (config.b == 2) value = pixelColor.b;
+ else if (config.b == 3) value = pixelColor.a;
+ else value = max(pixelColor.r, max(pixelColor.g, pixelColor.b));
+ if (config.a == 1) value = 1 - value;
+ return value;
+}
+
+float3 SampleInputRGB(Texture2D<float4> tex, float4 config, float2 uv) {
+ if (config.r == 0) {
+ return float3(config.g, config.g, config.g);
+ }
+ float4 pixelColor = float4(1, 1, 1, 1);
+ if (config.g == 0) pixelColor = tex.SampleLevel(pointClampSampler, uv, 0);
+ else pixelColor = pixelColor = tex.SampleLevel(linearClampSampler, uv, 0);
+ if (config.a == 1)
+ {
+ pixelColor.r = 1 - pixelColor.r;
+ pixelColor.g = 1 - pixelColor.g;
+ pixelColor.b = 1 - pixelColor.b;
+ }
+ return pixelColor;
+}
+
+[numthreads(8, 8, 1)]
+void CSMain(uint3 id : SV_DispatchThreadID)
+{
+ float2 uv = float2(id.x / Width, id.y / Height);
+ float4 pixel = float4(1, 1, 1, 1);
+ if (TakeRGBFromRTexture)
+ {
+ pixel.rgb = SampleInputRGB(R_Input, R_Config, uv);
+ pixel.a = SampleInput(G_Input, G_Config, uv);
+ }
+ else
+ {
+ pixel.r = SampleInput(R_Input, R_Config, uv);
+ pixel.g = SampleInput(G_Input, G_Config, uv);
+ pixel.b = SampleInput(B_Input, B_Config, uv);
+ pixel.a = SampleInput(A_Input, A_Config, uv);
+ }
+ Result[id.xy] = pixel;
+}
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryTexturePacker.compute.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryTexturePacker.compute.meta
new file mode 100644
index 00000000..f7b2c17c
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/ThryTexturePacker.compute.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 56f54c5664777a747b2552701571174d
+ComputeShaderImporter:
+ externalObjects: {}
+ currentAPIMask: 4
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Unity.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Unity.cs
new file mode 100644
index 00000000..294b05e8
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Unity.cs
@@ -0,0 +1,245 @@
+// Material/Shader Inspector for Unity 2017/2018
+// Copyright (C) 2019 Thryrallo
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using UnityEditor;
+using UnityEngine;
+
+namespace Thry
+{
+ public class UnityHelper
+ {
+ public static List<string> FindAssetsWithFilename(string filename)
+ {
+ string[] guids = AssetDatabase.FindAssets(Path.GetFileNameWithoutExtension(filename));
+ return guids.Select(g => AssetDatabase.GUIDToAssetPath(g)).Where(p => p.EndsWith(filename)).ToList();
+ }
+
+ public static void SetDefineSymbol(string symbol, bool active)
+ {
+ SetDefineSymbol(symbol, active, true);
+ }
+
+ public static void SetDefineSymbol(string symbol, bool active, bool refresh_if_changed)
+ {
+ try
+ {
+ string symbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(
+ BuildTargetGroup.Standalone);
+ if (!symbols.Contains(symbol) && active)
+ {
+ PlayerSettings.SetScriptingDefineSymbolsForGroup(
+ BuildTargetGroup.Standalone, symbols + ";" + symbol);
+ if(refresh_if_changed)
+ AssetDatabase.Refresh();
+ }
+ else if (symbols.Contains(symbol) && !active)
+ {
+ PlayerSettings.SetScriptingDefineSymbolsForGroup(
+ BuildTargetGroup.Standalone, Regex.Replace(symbols, @";?" + @symbol, ""));
+ if(refresh_if_changed)
+ AssetDatabase.Refresh();
+ }
+ }
+ catch (Exception e)
+ {
+ e.ToString();
+ }
+ }
+
+ public static void RemoveDefineSymbols()
+ {
+ UnityHelper.SetDefineSymbol(DEFINE_SYMBOLS.IMAGING_EXISTS, false);
+ }
+
+ public static void RepaintEditorWindow<T>() where T : EditorWindow
+ {
+ EditorWindow window = (EditorWindow)Resources.FindObjectsOfTypeAll<T>().FirstOrDefault();
+ if (window != null) window.Repaint();
+ }
+
+ public static string GetGUID(UnityEngine.Object o)
+ {
+ return AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(o));
+ }
+
+ public static WindowType FindOpenEditorWindow<WindowType>() where WindowType : EditorWindow
+ {
+ WindowType[] windows = Resources.FindObjectsOfTypeAll<WindowType>();
+ if (windows != null && windows.Length > 0)
+ {
+ return windows[0];
+ }
+ return null;
+ }
+
+ public static EditorWindow FindOpenEditorWindow(Type type)
+ {
+ UnityEngine.Object[] windows = Resources.FindObjectsOfTypeAll(type);
+ if (windows != null && windows.Length > 0)
+ {
+ return windows[0] as EditorWindow;
+ }
+ return null;
+ }
+
+ public static string GetCurrentAssetExplorerFolder()
+ {
+ if (Selection.activeObject) return "Assets";
+ string path = AssetDatabase.GetAssetPath(Selection.activeObject);
+ if (Directory.Exists(path)) return path;
+ else return Path.GetDirectoryName(path);
+ }
+ }
+
+ public class UnityFixer
+ {
+ public const string RSP_DRAWING_DLL_CODE = "\n-r:System.Drawing.dll";
+ public const string RSP_DRAWING_DLL_DEFINE_CODE = "\n-define:SYSTEM_DRAWING";
+ public const string RSP_DRAWING_DLL_REGEX = @"-r:\s*System\.Drawing\.dll";
+ public const string RSP_DRAWING_DLL_DEFINE_REGEX = @"-define:\s*SYSTEM_DRAWING";
+
+
+#if UNITY_2019_1_OR_NEWER
+ public const string RSP_FILENAME = "csc";
+#else
+ public const string RSP_FILENAME = "mcs";
+#endif
+
+ public static void OnAssetDeleteCheckDrawingDLL(string[] deleted_assets)
+ {
+ foreach (string path in deleted_assets)
+ {
+ if (path == PATH.RSP_NEEDED_PATH + RSP_FILENAME + ".rsp" || path.EndsWith("/System.Drawing.dll"))
+ UnityHelper.SetDefineSymbol(DEFINE_SYMBOLS.IMAGING_EXISTS, false, true);
+ }
+ }
+
+ public static void CheckAPICompatibility()
+ {
+ ApiCompatibilityLevel level = PlayerSettings.GetApiCompatibilityLevel(BuildTargetGroup.Standalone);
+ if (level == ApiCompatibilityLevel.NET_2_0_Subset)
+ PlayerSettings.SetApiCompatibilityLevel(BuildTargetGroup.Standalone, ApiCompatibilityLevel.NET_2_0);
+ }
+
+ public static void CheckDrawingDll()
+ {
+ string path = PATH.RSP_NEEDED_PATH + RSP_FILENAME + ".rsp";
+ bool refresh = true;
+ bool containsDLL = DoesRSPContainDrawingDLL(path);
+ bool containsDefine = DoesRSPContainDrawingDLLDefine(path);
+ if (!containsDefine && !containsDLL)
+ {
+ AddDrawingDLLToRSP(path);
+ AddDrawingDLLDefineToRSP(path);
+ }
+ else if (!containsDLL)
+ AddDrawingDLLToRSP(path);
+ else if (!containsDefine)
+ AddDrawingDLLDefineToRSP(path);
+ else
+ refresh = false;
+ if (refresh)
+ AssetDatabase.ImportAsset(path);
+ }
+
+
+
+ private static bool DoesRSPContainDrawingDLL(string rsp_path)
+ {
+ if (!File.Exists(rsp_path)) return false;
+ string rsp_data = FileHelper.ReadFileIntoString(rsp_path);
+ return (Regex.Match(rsp_data, RSP_DRAWING_DLL_REGEX).Success);
+ }
+
+ private static bool DoesRSPContainDrawingDLLDefine(string rsp_path)
+ {
+ if (!File.Exists(rsp_path)) return false;
+ string rsp_data = FileHelper.ReadFileIntoString(rsp_path);
+ return (Regex.Match(rsp_data, RSP_DRAWING_DLL_DEFINE_REGEX).Success);
+ }
+
+ private static void AddDrawingDLLToRSP(string rsp_path)
+ {
+ string rsp_data = FileHelper.ReadFileIntoString(rsp_path);
+ rsp_data += RSP_DRAWING_DLL_CODE;
+ FileHelper.WriteStringToFile(rsp_data, rsp_path);
+ }
+
+ private static void AddDrawingDLLDefineToRSP(string rsp_path)
+ {
+ string rsp_data = FileHelper.ReadFileIntoString(rsp_path);
+ rsp_data += RSP_DRAWING_DLL_DEFINE_CODE;
+ FileHelper.WriteStringToFile(rsp_data, rsp_path);
+ }
+ }
+
+ [InitializeOnLoad]
+ public class OnCompileHandler
+ {
+ static OnCompileHandler()
+ {
+ //Init Editor Variables with paths
+ ShaderEditor.GetShaderEditorDirectoryPath();
+
+ Config.OnCompile();
+ ModuleHandler.OnCompile();
+ TrashHandler.EmptyThryTrash();
+
+ UnityFixer.CheckAPICompatibility(); //check that Net_2.0 is ApiLevel
+ UnityFixer.CheckDrawingDll(); //check that drawing.dll is imported
+ }
+ }
+
+ public class AssetChangeHandler : AssetPostprocessor
+ {
+ static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
+ {
+ if (importedAssets.Length > 0)
+ AssetsImported(importedAssets);
+ if (deletedAssets.Length > 0)
+ AssetsDeleted(deletedAssets);
+ if (movedAssets.Length > 0)
+ AssetsMoved(movedAssets, movedFromAssetPaths);
+ }
+
+ private static void AssetsImported(string[] assets)
+ {
+ ShaderHelper.AssetsImported(assets);
+ }
+
+ private static void AssetsMoved(string[] movedAssets, string[] movedFromAssetPaths)
+ {
+ ShaderHelper.AssetsMoved(movedFromAssetPaths, movedAssets);
+ }
+
+ private static void AssetsDeleted(string[] assets)
+ {
+ ShaderHelper.AssetsDeleted(assets);
+ UnityFixer.OnAssetDeleteCheckDrawingDLL(assets);
+ if (CheckForEditorRemove(assets))
+ {
+ Debug.Log("ShaderEditor is being deleted.");
+ Config.Singleton.verion = "0";
+ Config.Singleton.Save();
+ ModuleHandler.OnEditorRemove();
+ }
+ }
+
+ private static bool CheckForEditorRemove(string[] assets)
+ {
+ string test_for = ShaderEditor.GetShaderEditorDirectoryPath() + "/Editor/ShaderEditor.cs";
+ foreach (string p in assets)
+ {
+ if (p == test_for)
+ return true;
+ }
+ return false;
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Unity.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Unity.cs.meta
new file mode 100644
index 00000000..571571d1
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/Unity.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: fcf19c9e085d3b442a2974d5c193c268
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/UploadAnchorOverrideSetter.cs b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/UploadAnchorOverrideSetter.cs
new file mode 100644
index 00000000..e0169abd
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/UploadAnchorOverrideSetter.cs
@@ -0,0 +1,102 @@
+#if VRC_SDK_VRCSDK2 || VRC_SDK_VRCSDK3
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using Thry;
+using UnityEditor;
+using UnityEngine;
+using VRC.SDKBase.Editor.BuildPipeline;
+
+namespace Pumkin.VRCCallbacks
+{
+ class UploadAnchorOverrideSetter : IVRCSDKPreprocessAvatarCallback
+ {
+ public int callbackOrder => 0;
+
+ string DialogTitle => Locale.editor.Get("autoAnchorDialog_Title");
+ string DialogMessage => Locale.editor.Get("autoAnchorDialog_Text");
+ string DialogYes => Locale.editor.Get("yes");
+ string DialogNo => Locale.editor.Get("no");
+ string ErrorNotHumanoid => Locale.editor.Get("autoAnchorError_NotHumanoid");
+
+ bool Enabled
+ {
+ get => Config.Singleton.autoSetAnchorOverride;
+ set => Config.Singleton.autoSetAnchorOverride = value;
+ }
+
+ bool AskedOnce
+ {
+ get => Config.Singleton.autoSetAnchorAskedOnce;
+ set => Config.Singleton.autoSetAnchorAskedOnce = value;
+ }
+
+ HumanBodyBones HumanBoneAnchor => Config.Singleton.humanBoneAnchor;
+ string AnchorName => Config.Singleton.anchorOverrideObjectName;
+
+
+ public bool OnPreprocessAvatar(GameObject avatarGameObject)
+ {
+ try
+ {
+ Renderer[] renderersWithNoAnchors = null;
+ if(!AskedOnce) // If we haven't already asked, only display dialog once a renderer with no anchors is found
+ {
+ renderersWithNoAnchors = avatarGameObject.GetComponentsInChildren<Renderer>(true)?.Where(r => r.probeAnchor == null).ToArray();
+
+ if(renderersWithNoAnchors == null || renderersWithNoAnchors.Length == 0)
+ return true;
+
+ Enabled = EditorUtility.DisplayDialog(DialogTitle, DialogMessage, DialogYes, DialogNo);
+ AskedOnce = true;
+ Config.Singleton.Save();
+ }
+
+ if(!Enabled)
+ return true;
+
+ if(renderersWithNoAnchors == null)
+ renderersWithNoAnchors = avatarGameObject.GetComponentsInChildren<Renderer>(true)?.Where(r => r.probeAnchor == null).ToArray();
+
+ if(renderersWithNoAnchors == null || renderersWithNoAnchors.Length == 0)
+ return true;
+
+ Transform anchorObject = null;
+
+ string anchorName = AnchorName;
+ if(!string.IsNullOrEmpty(anchorName))
+ {
+ var children = avatarGameObject.GetComponentsInChildren<Transform>().Skip(1);
+ anchorObject = children.FirstOrDefault(t => t.name.Equals(anchorName, StringComparison.OrdinalIgnoreCase));
+ }
+
+ if(!anchorObject)
+ {
+ var anim = avatarGameObject.GetComponent<Animator>();
+ if(anim && anim.isHuman)
+ anchorObject = anim.GetBoneTransform(HumanBoneAnchor);
+ else
+ {
+ Debug.LogErrorFormat(ErrorNotHumanoid, avatarGameObject.name);
+ return true;
+ }
+ }
+
+ foreach(var render in renderersWithNoAnchors)
+ {
+ if(render.probeAnchor != null)
+ continue;
+
+ render.probeAnchor = anchorObject;
+ }
+ }
+ catch(Exception ex)
+ {
+ Debug.LogException(ex);
+ }
+ return true;
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/UploadAnchorOverrideSetter.cs.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/UploadAnchorOverrideSetter.cs.meta
new file mode 100644
index 00000000..23529898
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Editor/UploadAnchorOverrideSetter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9d3f2f889ac34d2cb6bb8b679b706701
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples.meta
new file mode 100644
index 00000000..5046ab30
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d8d86a6307e038842b84cf3cc1cc093a
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/Example1.shader b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/Example1.shader
new file mode 100644
index 00000000..0ea4b748
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/Example1.shader
@@ -0,0 +1,440 @@
+Shader "Thry/Example 1"
+{
+ Properties
+ {
+ [HideInInspector] shader_is_using_thry_editor("", Float)=0
+ [HideInInspector] shader_master_label("<color=#ff0000ff>Example 1</color>", Float) = 0
+ [HideInInspector] shader_presets("ThryPresetsExample", Float) = 0
+ [HideInInspector] shader_properties_label_file("ThryLabelExample", Float) = 0
+
+ [HideInInspector] footer_website("", Float) = 0
+ [HideInInspector] footer_github("", Float) = 0
+
+
+ shader_properties_locale("locale::locale--{file_name:thry_locale_example}", Float) = 0
+ [Enum(Cutout,0,Transparent,1)]variant_selector("Variant--{on_value_actions:[{value:0,actions:[{type:SET_PROPERTY,data:_ZWrite=1},{type:SET_SHADER,data:Thry/Example 1}]},{value:1,actions:[{type:SET_PROPERTY,data:_ZWrite=0},{type:SET_SHADER,data:Thry/Example 2}]}]}",Float) = 0
+
+ [ThryWideEnum(Opaque, 0, Cutout, 1, Fade, 2, Transparent, 3, Additive, 4, Soft Additive, 5, Multiplicative, 6, 2x Multiplicative, 7, Multiplicative Grab Pass, 8)]_Mode("Rendering Preset--{on_value:''
+ 0,render_queue = 2000,render_type = Opaque,_BlendOp = 0,_BlendOpAlpha = 0,_Cutoff = 0,_SrcBlend = 1,_DstBlend = 0,_AlphaToMask = 0,_ZWrite = 1,_ZTest = 4,_AlphaPremultiply = 0;
+ 1,render_queue = 2460,render_type = TransparentCutout,_BlendOp = 0,_BlendOpAlpha = 0,_Cutoff = 0.5,_SrcBlend = 1,_DstBlend = 0,_AlphaToMask = 1,_ZWrite = 1,_ZTest = 4,_AlphaPremultiply = 0
+ '' }", Int) = 0
+
+ [HideInInspector] m_mainOptions("Main", Float) = 0
+ _Color("Color & Alpha", Color) = (1, 1, 1, 1)
+ [Helpbox]_HelpboxForSomething("Alpha is controlled in the color", Float) = 1
+ _Saturation("Saturation", Range(-1, 1)) = 0
+ _MainVertexColoring("Use Vertex Color", Range(0,1)) = 0
+ _MainEmissionStrength("Basic Emission", Range(0, 20)) = 0
+ [Curve]_MainTex("Texture", 2D) = "white" { }
+ [PanningTexture][Normal]_BumpMap("Normal Map", 2D) = "bump" { }
+ [Enum(UV0, 0, UV1, 1, UV2, 2, UV3, 3)] _BumpMapUV("Normal UV#", Int) = 0
+ [HideInInspector][Vector2]_MainNormalPan("Panning", Vector) = (0, 0, 0, 0)
+ _BumpScale("Normal Intensity", Range(0, 10)) = 1
+ _AlphaMask("Alpha Mask", 2D) = "white" { }
+ [Vector2]_GlobalPanSpeed("Global Pan Speed", Vector) = (0, 0, 0, 0)
+
+ [HideInInspector] m_start_Alpha("Alpha Options--{altClick:{type:URL,data:https://thryrallo.de}}", Float) = 0
+ _Clip("Alpha Cuttoff", Range(0, 1.001)) = 0.5
+ [Toggle(_)]_ForceOpaque("Force Opaque", Float) = 0
+ [Toggle(_)]_MainAlphaToCoverage("Alpha To Coverage", Float) = 1
+ _MainMipScale("Mip Level Alpha Scale", Range(0, 1)) = 0.25
+ [HideInInspector] m_end_Alpha("Alpha Options", Float) = 0
+
+ [HideInInspector] m_start_DetailOptions("Details", Float) = 0
+ _DetailMask("Detail Mask (R:Texture, G:Normal)", 2D) = "white" { }
+ [PanningTexture]_DetailTex("Detail Texture", 2D) = "gray" { }
+ [HideInInspector][Vector2]_DetailTexturePan("Panning", Vector) = (0, 0, 0, 0)
+ [Enum(UV0, 0, UV1, 1, UV2, 2, UV3, 3)] _DetailTexUV("Detail Tex UV#", Int) = 0
+ _DetailTexIntensity("Detail Tex Intensity", Range(0, 10)) = 1
+ _DetailBrightness("Detail Brightness:", Range(0, 2)) = 1
+ _DetailTint("Detail Tint", Color) = (1, 1, 1)
+ [Normal]_DetailNormalMap("Detail Normal", 2D) = "bump" { }
+ [Enum(UV0, 0, UV1, 1, UV2, 2, UV3, 3)] _DetailNormalUV("Detail Normal UV#", Int) = 0
+ _DetailNormalMapScale("Detail Normal Intensity", Range(0, 10)) = 1
+ [HideInInspector][Vector2]_MainDetailNormalPan("Panning", Vector) = (0, 0, 0, 0)
+ [HideInInspector] m_end_DetailOptions("Details", Float) = 0
+
+ [HideInInspector] m_lightingOptions("Lighting Options", Float) = 0
+ [HideInInspector] m_start_Lighting("Light and Shadow", Float) = 0
+ [Toggle(_NORMALMAP)]_EnableLighting("Enable Lighting", Float) = 1
+ [HideInInspector] g_start_l("", Int) = 0
+ [Enum(Natural, 0, Controlled, 1, Standardish, 2)] _LightingType("Lighting Type", Int) = 1
+ [Gradient]_ToonRamp("Lighting Ramp", 2D) = "white" { }
+ _LightingShadowMask("Shadow Mask (R)", 2D) = "white" { }
+ _ShadowStrength("Shadow Strength", Range(0, 1)) = .2
+ _ShadowOffset("Shadow Offset", Range(-1, 1)) = 0
+ _AOMap("AO Map", 2D) = "white" { }
+ [Enum(UV0, 0, UV1, 1, UV2, 2, UV3, 3)] _LightingAOUV("AO Map UV#", Int) = 0
+ _AOStrength("AO Strength", Range(0, 1)) = 1
+ _LightingMinLightBrightness("Min Brightness", Range(0,1)) = 0
+ [HideInInspector] m_start_lightingStandard("Standardish Settings", Float) = 0
+ _LightingStandardSmoothness("Smoothness", Range(0, 1)) = 0
+ [HideInInspector] m_end_lightingStandard("Standardish Settings", Float) = 0
+ [HideInInspector] m_start_lightingAdvanced("Advanced", Float) = 0
+ _LightingIndirectContribution("Indirect Contribution", Range(0, 1)) = .25
+ _AdditiveSoftness("Additive Softness", Range(0, 0.5)) = 0.005
+ _AdditiveOffset("Additive Offset", Range(-0.5, 0.5)) = 0
+ _LightingAdditiveIntensity("Additive Intensity", Range(0,1)) = 1
+ _AttenuationMultiplier("Attenuation", Range(0, 1)) = 0
+ [HideInInspector] m_end_lightingAdvanced("Advanced", Float) = 0
+ [HideInInspector] m_start_lightingBeta("Beta", Float) = 0
+ [Toggle(_)]_LightingStandardControlsToon("Standard Lighting Controls Toon Ramp", Float) = 0
+ [HideInInspector] m_end_lightingBeta("Beta", Float) = 0
+ [HideInInspector] g_end_l("", Int) = 0
+ [HideInInspector] m_end_Lighting("Light and Shadow", Float) = 0
+
+ [HideInInspector] m_start_subsurface("Subsurface Scattering", Float) = 0
+ [Toggle(_TERRAIN_NORMAL_MAP)]_EnableSSS("Enable Subsurface Scattering", Float) = 0
+ _SSSColor("Subsurface Color", Color) = (1, 1, 1, 1)
+ _SSSThicknessMap("Thickness Map", 2D) = "black" { }
+ _SSSThicknessMod("Thickness mod", Range(-1, 1)) = 0
+ _SSSSCale("Light Strength", Range(0, 1)) = 0
+ _SSSPower("Light Spread", Range(1, 100)) = 1
+ _SSSDistortion("Light Distortion", Range(0, 1)) = 0
+ [HideInInspector] m_end_subsurface("Subsurface Scattering", Float) = 0
+
+ [HideInInspector] m_start_rimLightOptions("Rim Lighting", Float) = 0
+ [Toggle(_GLOSSYREFLECTIONS_OFF)]_EnableRimLighting("Enable Rim Lighting", Float) = 0
+ [Toggle(_)]_RimLightingInvert("Invert Rim Lighting", Float) = 0
+ _RimLightColor("Rim Color", Color) = (1, 1, 1, 1)
+ _RimWidth("Rim Width", Range(0, 1)) = 0.8
+ _RimSharpness("Rim Sharpness", Range(0, 1)) = .25
+ _RimStrength("Rim Emission", Range(0, 20)) = 0
+ _RimBrighten("Rim Color Brighten", Range(0, 3)) = 0
+ _RimLightColorBias("Rim Color Bias", Range(0, 1)) = 0
+ [PanningTexture]_RimTex("Rim Texture", 2D) = "white" { }
+ _RimMask("Rim Mask", 2D) = "white" { }
+ [HideInInspector][Vector2]_RimTexPanSpeed("Panning", Vector) = (0, 0, 0, 0)
+ [HideInInspector] m_start_reflectionRim("Environmental Rim", Float) = 0
+ [Toggle(_)]_EnableEnvironmentalRim("Enable Environmental Rim", Float) = 0
+ _RimEnviroMask("Mask", 2D) = "white" { }
+ _RimEnviroBlur("Blur", Range(0, 1)) = 0.7
+ _RimEnviroWidth("Rim Width", Range(0, 1)) = 0.45
+ _RimEnviroSharpness("Rim Sharpness", Range(0, 1)) = 0
+ _RimEnviroMinBrightness("Min Brightness Threshold", Range(0, 2)) = 0
+ [HideInInspector] m_end_reflectionRim("Environmental Rim", Float) = 0
+ [HideInInspector] m_start_rimWidthNoise("Width Noise", Float) = 0
+ [PanningTexture]_RimWidthNoiseTexture("Rim Width Noise", 2D) = "black" { }
+ _RimWidthNoiseStrength("Intensity", Range(0, 1)) = 0.1
+ [HideInInspector][Vector2]_RimWidthNoisePan("Panning", Vector) = (0, 0, 0, 0)
+ [HideInInspector] m_end_rimWidthNoise("Width Noise", Float) = 0
+ [HideInInspector] m_start_ShadowMix("Shadow Mix", Float) = 0
+ _ShadowMix("Shadow Mix In", Range(0, 1)) = 0
+ _ShadowMixThreshold("Shadow Mix Threshold", Range(0, 1)) = .5
+ _ShadowMixWidthMod("Shadow Mix Width Mod", Range(0, 10)) = .5
+ [HideInInspector] m_end_ShadowMix("Shadow Mix", Float) = 0
+ [HideInInspector] m_end_rimLightOptions("Rim Lighting", Float) = 0
+
+ [HideInInspector] m_start_bakedLighting("Baked Lighting", Float) = 0
+ _GIEmissionMultiplier("GI Emission Multiplier", Float) = 1
+ [HideInInspector] DSGI("DSGI", Float) = 0 //add this property for double sided illumination settings to be shown
+ [HideInInspector] LightmapFlags("Lightmap Flags", Float) = 0 //add this property for lightmap flags settings to be shown
+ [HideInInspector] m_end_bakedLighting("Baked Lighting", Float) = 0
+
+ [HideInInspector] m_reflectionOptions("Reflections", Float) = 0
+ [HideInInspector] m_start_Metallic("Metallics", Float) = 0
+ [Toggle(_METALLICGLOSSMAP)]_EnableMetallic("Enable Metallics", Float) = 0
+ _CubeMap("Baked CubeMap", Cube) = "" { }
+ [Toggle(_)]_SampleWorld("Force Baked Cubemap", Range(0, 1)) = 0
+ _MetalReflectionTint("Reflection Tint", Color) = (1, 1, 1)
+ _MetallicMask("Metallic Mask", 2D) = "white" { }
+ _Metallic("Metallic", Range(0, 1)) = 0
+ _SmoothnessMask("Smoothness Map", 2D) = "white" { }
+ [Toggle(_)]_InvertSmoothness("Invert Smoothness Map", Range(0, 1)) = 0
+ _Smoothness("Smoothness", Range(0, 1)) = 0
+ [HideInInspector] m_end_Metallic("Metallics", Float) = 0
+
+ [HideInInspector] m_start_clearCoat("Clear Coat", Float) = 0
+ [Toggle(_COLORCOLOR_ON)]_EnableClearCoat("Enable Clear Coat", Float) = 0
+ [Enum(Vertex, 0, Pixel, 1)] _ClearCoatNormalToUse("What Normal?", Int) = 0
+ _ClearCoatCubeMap("Baked CubeMap", Cube) = "" { }
+ [Toggle(_)]_ClearCoatSampleWorld("Force Baked Cubemap", Range(0, 1)) = 0
+ _ClearCoatTint("Reflection Tint", Color) = (1, 1, 1)
+ _ClearCoatMask("Mask", 2D) = "white" { }
+ _ClearCoat("Clear Coat", Range(0, 1)) = 1
+ _ClearCoatSmoothnessMask("Smoothness Map", 2D) = "white" { }
+ [Toggle(_)]_ClearCoatInvertSmoothness("Invert Smoothness Map", Range(0, 1)) = 0
+ _ClearCoatSmoothness("Smoothness", Range(0, 1)) = 0
+ [Toggle(_)]_ClearCoatForceLighting("Force Lighting", Float) = 0
+ [HideInInspector] m_end_clearCoat("Clear Coat", Float) = 0
+
+ [HideInInspector] m_start_matcap("Matcap / Sphere Textures", Float) = 0
+ [Toggle(_COLORADDSUBDIFF_ON)]_MatcapEnable("Enable Matcap", Float) = 0
+ _MatcapColor("Color", Color) = (1, 1, 1, 1)
+ [TextureNoSO]_Matcap("Matcap", 2D) = "white" { }
+ _MatcapBorder("Border", Range(0, .5)) = 0.43
+ _MatcapMask("Mask", 2D) = "white" { }
+ _MatcapIntensity("Intensity", Range(0, 5)) = 1
+ _MatcapLightMask("Hide in Shadow", Range(0, 1)) = 0
+ _MatcapReplace("Replace With Matcap", Range(0, 1)) = 1
+ _MatcapMultiply("Multiply Matcap", Range(0, 1)) = 0
+ _MatcapAdd("Add Matcap", Range(0, 1)) = 0
+ [Enum(Vertex, 0, Pixel, 1)] _MatcapNormal("Normal to use", Int) = 1
+ [HideInInspector] m_end_matcap("Matcap", Float) = 0
+ [HideInInspector] m_start_Matcap2("Matcap 2", Float) = 0
+ [Toggle(_)]_Matcap2Enable("Enable Matcap 2", Float) = 0
+ _Matcap2Color("Color", Color) = (1, 1, 1, 1)
+ [TextureNoSO]_Matcap2("Matcap", 2D) = "white" { }
+ _Matcap2Border("Border", Range(0, .5)) = 0.43
+ _Matcap2Mask("Mask", 2D) = "white" { }
+ _Matcap2Intensity("Intensity", Range(0, 5)) = 1
+ _Matcap2LightMask("Hide in Shadow", Range(0, 1)) = 0
+ _Matcap2Replace("Replace With Matcap", Range(0, 1)) = 0
+ _Matcap2Multiply("Multiply Matcap", Range(0, 1)) = 0
+ _Matcap2Add("Add Matcap", Range(0, 1)) = 0
+ [Enum(Vertex, 0, Pixel, 1)] _Matcap2Normal("Normal to use", Int) = 1
+ [HideInInspector] m_end_Matcap2("Matcap 2", Float) = 0
+
+ [HideInInspector] m_start_specular("Specular Reflections", Float) = 0
+ [Toggle(_SPECGLOSSMAP)]_EnableSpecular("Enable Specular", Float) = 0
+ [Enum(Realistic, 1, Toon, 2, Anisotropic, 3)] _SpecularType("Specular Type", Int) = 1
+ _SpecularMinLightBrightness("Min Light Brightness", Range(0, 1)) = 0
+ _SpecularTint("Specular Tint", Color) = (.2, .2, .2, 1)
+ _SpecularMixAlbedoIntoTint("Mix Material Color Into Tint", Range(0, 1)) = 0
+ _SpecularSmoothness("Smoothness", Range(-2, 1)) = .75
+ _SpecularMap("Specular Map", 2D) = "white" { }
+ [Toggle(_)]_SpecularInvertSmoothness("Invert Smoothness", Float) = 0
+ _SpecularMask("Specular Mask", 2D) = "white" { }
+ [Enum(Alpha, 0, Grayscale, 1)] _SmoothnessFrom("Smoothness From", Int) = 1
+ [HideInInspector] m_start_SpecularToon("Toon", Float) = 0
+ [MultiSlider]_SpecularToonInnerOuter("Inner/Outer Edge", Vector) = (0.25, 0.3, 0, 1)
+ [HideInInspector] m_end_SpecularToon("Toon", Float) = 0
+ [HideInInspector] m_start_Anisotropic("Anisotropic", Float) = 0
+ [Enum(Tangent, 0, Bitangent, 1)] _SpecWhatTangent("(Bi)Tangent?", Int) = 0
+ _AnisoSpec1Alpha("Spec1 Alpha", Range(0, 1)) = 1
+ _AnisoSpec2Alpha("Spec2 Alpha", Range(0, 1)) = 1
+ //_Spec1Offset ("Spec1 Offset", Float) = 0
+ //_Spec1JitterStrength ("Spec1 Jitter Strength", Float) = 1.0
+ _Spec2Smoothness("Spec2 Smoothness", Range(0, 1)) = 0
+ //_Spec2Offset ("Spec2 Offset", Float) = 0
+ //_Spec2JitterStrength ("Spec2 Jitter Strength", Float) = 1.0
+ [Toggle(_)]_AnisoUseTangentMap("Use Directional Map?", Float) = 0
+ _AnisoTangentMap("Anisotropic Directional Map", 2D) = "bump" { }
+ //_ShiftTexture ("Shift Texture", 2D) = "black" { }
+ [HideInInspector] m_end_Anisotropic("Anisotropic", Float) = 0
+ [HideInInspector] m_end_specular("Specular Reflections", Float) = 0
+
+ [HideInInspector] m_Special_Effects("Special Effects", Float) = 0
+ [HideInInspector] m_start_emissionOptions("Emission / Glow", Float) = 0
+ [Toggle(_EMISSION)]_EnableEmission("Enable Emission", Float) = 0
+ [Enum(UV0, 0, UV1, 1, UV2, 2, UV3, 3)] _EmissionUV("Emission UV#", Int) = 0
+ [HDR]_EmissionColor("Emission Color", Color) = (1, 1, 1, 1)
+ [PanningTexture]_EmissionMap("Emission Map", 2D) = "white" { }
+ [PanningTexture]_EmissionMask("Emission Mask", 2D) = "white" { }
+ [HideInInspector][Vector2]_EmissionMapPan("Panning", Vector) = (0, 0, 0, 0)
+ [HideInInspector][Vector2]_EmissionMaskPan("Panning", Vector) = (0, 0, 0, 0)
+ _EmissionStrength("Emission Strength", Range(0, 20)) = 0
+ // Inward out emission
+ [HideInInspector] m_start_CenterOutEmission("Center Out Emission", Float) = 0
+ [Toggle(_)]_EmissionCenterOutEnabled("Enable Center Out", Float) = 0
+ _EmissionCenterOutSpeed("Flow Speed", Float) = 5
+ [HideInInspector] m_end_CenterOutEmission("inward out emission", Float) = 0
+ //Glow in the dark Emission
+ [HideInInspector] m_start_glowInDarkEmissionOptions("Glow In The Dark Emission (Requires Lighting Enabled)", Float) = 0
+ [Toggle(_)]_EnableGITDEmission("Enable Glow In The Dark", Float) = 0
+ [Enum(World, 0, Mesh, 1)] _GITDEWorldOrMesh("Lighting Type", Int) = 0
+ _GITDEMinEmissionMultiplier("Min Emission Multiplier", Range(0, 1)) = 1
+ _GITDEMaxEmissionMultiplier("Max Emission Multiplier", Range(0, 1)) = 0
+ _GITDEMinLight("Min Lighting", Range(0, 1)) = 0
+ _GITDEMaxLight("Max Lighting", Range(0, 1)) = 1
+ [HideInInspector] m_end_glowInDarkEmissionOptions("Glow In The Dark Emission (Requires Lighting Enabled)", Float) = 0
+
+ [HideInInspector] m_start_blinkingEmissionOptions("Blinking Emission", Float) = 0
+ _EmissiveBlink_Min("Emissive Blink Min", Float) = 1
+ _EmissiveBlink_Max("Emissive Blink Max", Float) = 1
+ _EmissiveBlink_Velocity("Emissive Blink Velocity", Float) = 4
+ [HideInInspector] m_end_blinkingEmissionOptions("Blinking Emission", Float) = 0
+
+ [HideInInspector] m_start_scrollingEmissionOptions("Scrolling Emission", Float) = 0
+ [Toggle(_)] _ScrollingEmission("Enable Scrolling Emission", Float) = 0
+ _EmissiveScroll_Direction("Emissive Scroll Direction", Vector) = (0, -10, 0, 0)
+ _EmissiveScroll_Width("Emissive Scroll Width", Float) = 10
+ _EmissiveScroll_Velocity("Emissive Scroll Velocity", Float) = 10
+ _EmissiveScroll_Interval("Emissive Scroll Interval", Float) = 20
+ [HideInInspector] m_end_scrollingEmissionOptions("Scrolling Emission", Float) = 0
+ [HideInInspector] m_end_emissionOptions("Emission / Glow", Float) = 0
+
+ [HideInInspector] m_start_flipBook("Flipbook", Float) = 0
+ [Toggle(_FLIPBOOK_BLENDING)]_EnableFlipbook("Enable Flipbook", Float) = 0
+ [Enum(UV0, 0, UV1, 1, UV2, 2, UV3, 3)] _FlipbookUV("Flipbook UV#", Int) = 0
+ [TextureArray]_FlipbookTexArray("Texture Array--{reference_property:_FlipbookTotalFrames}", 2DArray) = "" { }
+ _FlipbookColor("Color & alpha", Color) = (1, 1, 1, 1)
+ _FlipbookTotalFrames("Total Frames", Int) = 1
+ _FlipbookFPS("FPS", Float) = 30.0
+ _FlipbookScaleOffset("Scale | Offset", Vector) = (1, 1, 0, 0)
+ [Toggle(_)]_FlipbookTiled("Tiled?", Float) = 0
+ _FlipbookEmissionStrength("Emission Strength", Range(0, 20)) = 0
+ _FlipbookRotation("Rotation", Range(0, 360)) = 0
+ _FlipbookReplace("Replace", Range(0, 1)) = 1
+ _FlipbookMultiply("Multiply", Range(0, 1)) = 0
+ _FlipbookAdd("Add", Range(0, 1)) = 0
+ [HideInInspector] m_start_manualFlipbookControl("Manual Control", Float) = 0
+ _FlipbookCurrentFrame("Current Frame", Float) = -1
+ [HideInInspector] m_end_manualFlipbookControl("Manual Control", Float) = 0
+ [HideInInspector] m_end_flipBook("Flipbook", Float) = 0
+
+ [HideInInspector] m_start_dissolve("Dissolve", Float) = 0
+ [Toggle(_ALPHABLEND_ON)]_EnableDissolve("Enable Dissolve", Float) = 0
+ [Enum(Basic, 1, Point2Point, 2)] _DissolveType("Dissolve Type", Int) = 1
+ _DissolveEdgeWidth("Edge Width", Range(0, .5)) = 0.025
+ _DissolveEdgeHardness("Edge Hardness", Range(0, 1)) = 0.5
+ _DissolveEdgeColor("Edge Color", Color) = (1, 1, 1, 1)
+ [Gradient]_DissolveEdgeGradient("Edge Gradient", 2D) = "white" { }
+ _DissolveEdgeEmission("Edge Emission", Range(0, 20)) = 0
+ _DissolveTextureColor("Dissolve to Color", Color) = (1, 1, 1, 1)
+ [PanningTexture]_DissolveToTexture("Dissolve to Texture", 2D) = "white" { }
+ _DissolveToEmissionStrength("Dissolve to Emission Strength", Range(0, 20)) = 0
+ [HideInInspector][Vector2]_DissolveToPanning("Panning", Vector) = (0, 0, 0, 0)
+ [PanningTexture]_DissolveNoiseTexture("Dissolve Noise", 2D) = "white" { }
+ [Toggle(_)]_DissolveInvertNoise("Invert Noise", Float) = 0
+ [PanningTexture]_DissolveDetailNoise("Dissolve Detail Noise", 2D) = "black" { }
+ [Toggle(_)]_DissolveInvertDetailNoise("Invert Detail Noise", Float) = 0
+ _DissolveDetailStrength("Dissolve Detail Strength", Range(0, 1)) = 0.1
+ [HideInInspector][Vector2]_DissolveNoisePan("Panning", Vector) = (0, 0, 0, 0)
+ [HideInInspector][Vector2]_DissolveDetailPan("Panning", Vector) = (0, 0, 0, 0)
+ _DissolveAlpha("Dissolve Alpha", Range(0, 1)) = 0
+ _DissolveMask("Dissolve Mask", 2D) = "white" { }
+ [Toggle(_)]_ContinuousDissolve("Continuous Dissolve Speed", Float) = 0
+ [HideInInspector] m_start_pointToPoint("point to point", Float) = 0
+ [Enum(Local, 0, World, 1)] _DissolveP2PWorldLocal("World/Local", Int) = 0
+ _DissolveP2PEdgeLength("Edge Length", Float) = 0.1
+ [Vector3]_DissolveStartPoint("Start Point", Vector) = (0, -1, 0, 0)
+ [Vector3]_DissolveEndPoint("End Point", Vector) = (0, 1, 0, 0)
+ [HideInInspector] m_end_pointToPoint("Point To Point", Float) = 0
+ [HideInInspector] m_end_dissolve("Dissolve", Float) = 0
+
+ [HideInInspector] m_start_panosphereOptions("Panosphere / Cubemaps", Float) = 0
+ [Toggle(_DETAIL_MULX2)]_PanoToggle("Enable Panosphere", Float) = 0
+ _PanosphereColor("Color", Color) = (1, 1, 1, 1)
+ _PanosphereTexture("Texture", 2D) = "white" { }
+ _PanoMapTexture("Mask", 2D) = "white" { }
+ _PanoEmission("Emission Strength", Range(0, 10)) = 0
+ _PanoBlend("Alpha", Range(0, 1)) = 0
+ [Vector3]_PanospherePan("Pan Speed", Vector) = (0, 0, 0, 0)
+ [Toggle(_)]_PanoCubeMapToggle("Use Cubemap", Float) = 0
+ [TextureNoSO]_PanoCubeMap("CubeMap", Cube) = "" { }
+ [HideInInspector] m_end_panosphereOptions("Panosphere / Cubemaps", Float) = 0
+
+ [HideInInspector] m_start_mirrorOptions("Mirror", Float) = 0
+ [Toggle(_REQUIRE_UV2)]_EnableMirrorOptions("Enable Mirror Options", Float) = 0
+ [Enum(ShowInBoth, 0, ShowOnlyInMirror, 1, DontShowInMirror, 2)] _Mirror("Show in mirror", Int) = 0
+ [Toggle(_)]_EnableMirrorTexture("Enable Mirror Texture", Float) = 0
+ _MirrorTexture("Mirror Tex", 2D) = "white" { }
+ [HideInInspector] m_end_mirrorOptions("Mirror", Float) = 0
+
+ [HideInInspector] m_start_distanceFade("Distance Fade", Float) = 0
+ _MainMinAlpha("Minimum Alpha", Range(0, 1)) = 0
+ _MainFadeTexture("Fade Map", 2D) = "white" { }
+ [Vector2]_MainDistanceFade("Distance Fade X to Y", Vector) = (0, 0, 0, 0)
+ [HideInInspector] m_end_distanceFade("Distance Fade", Float) = 0
+
+ [HideInInspector] m_start_angularFade("Angular Fade", Float) = 0
+ [Toggle(_SUNDISK_NONE)]_EnableRandom("Enable Angular Fade", Float) = 0
+ [Enum(Camera Face Model, 0, Model Face Camera, 1, Face Each Other, 2)] _AngleType("Angle Type", Int) = 0
+ [Enum(Model, 0, Vertex, 1)] _AngleCompareTo("Model or Vert Positon", Int) = 0
+ [Vector3]_AngleForwardDirection("Forward Direction", Vector) = (0, 0, 1, 0)
+ _CameraAngleMin("Camera Angle Min", Range(0, 180)) = 45
+ _CameraAngleMax("Camera Angle Max", Range(0, 180)) = 90
+ _ModelAngleMin("Model Angle Min", Range(0, 180)) = 45
+ _ModelAngleMax("Model Angle Max", Range(0, 180)) = 90
+ _AngleMinAlpha("Min Alpha", Range(0, 1)) = 0
+ [HideInInspector] m_end_angularFade("Angular Fade", Float) = 0
+ // End Special Effects
+
+ [HideInInspector] m_parallaxMap("Parallax", Float) = 0
+ [Toggle(_PARALLAXMAP)]_ParallaxMap("Enable Parallax FX", Float) = 0
+ [Toggle(_)]_ParallaxHeightMapEnabled("Enable Parallax Height", Float) = 0
+ [Toggle(_)]_ParallaxInternalMapEnabled("Enable Parallax Internal", Float) = 0
+ [HideInInspector] m_start_parallaxHeightmap("Heightmap", Float) = 0
+ _ParallaxHeightMap("Height Map", 2D) = "black" { }
+ _ParallaxStrength("Parallax Strength", Range(0, 1)) = 0
+ [HideInInspector] m_end_parallaxHeightmap("Heightmap", Float) = 0
+ [HideInInspector] m_start_parallaxInternal("Internal", Float) = 0
+ [Enum(Basic, 0, HeightMap, 1)] _ParallaxInternalHeightmapMode("Parallax Mode", Int) = 0
+ [Toggle(_)]_ParallaxInternalHeightFromAlpha("HeightFromAlpha", Float) = 0
+ _ParallaxInternalMap("Internal Map", 2D) = "black" { }
+ _ParallaxInternalIterations("Parallax Internal Iterations", Range(1, 50)) = 1
+ _ParallaxInternalMinDepth("Min Depth", Float) = 0
+ _ParallaxInternalMaxDepth("Max Depth", Float) = 1
+ _ParallaxInternalMinFade("Min Depth Brightness", Range(0, 5)) = 0
+ _ParallaxInternalMaxFade("Max Depth Brightness", Range(0, 5)) = 1
+ _ParallaxInternalMinColor("Min Depth Color", Color) = (1, 1, 1, 1)
+ _ParallaxInternalMaxColor("Max Depth Color", Color) = (1, 1, 1, 1)
+ [Vector2]_ParallaxInternalPanSpeed("Pan Speed", Vector) = (0, 0, 0, 0)
+ [Vector2]_ParallaxInternalPanDepthSpeed("Per Level Speed Multiplier", Vector) = (0, 0, 0, 0)
+ [HideInInspector] m_end_parallaxInternal("Internal", Float) = 0
+ [HideInInspector] m_start_parallaxAdvanced("Advanced", Float) = 0
+ _ParallaxBias("Parallax Bias (0.42)", Float) = 0.42
+ [HideInInspector] m_end_parallaxAdvanced("Advanced", Float) = 0
+
+ [HideInInspector] m_renderingOptions("Rendering Options", Float) = 0
+ [Enum(UnityEngine.Rendering.CullMode)] _Cull("Cull", Float) = 2
+ [Enum(UnityEngine.Rendering.CompareFunction)] _ZTest("ZTest", Float) = 4
+ [Enum(UnityEngine.Rendering.BlendMode)] _SourceBlend("Source Blend", Float) = 5
+ [Enum(UnityEngine.Rendering.BlendMode)] _DestinationBlend("Destination Blend", Float) = 10
+ [Enum(Off, 0, On, 1)] _ZWrite("ZWrite", Int) = 1
+ _ZBias("ZBias", Float) = 0.0
+ [Toggle(_)]_IgnoreFog("Ignore Fog", Float) = 0
+ //[HideInInspector] Instancing ("Instancing", Float) = 0 //add this property for instancing variants settings to be shown
+
+ [HideInInspector] m_start_StencilPassOptions("Stencil", Float) = 0
+ [IntRange] _StencilRef("Stencil Reference Value", Range(0, 255)) = 0
+ //[IntRange] _StencilReadMaskRef ("Stencil ReadMask Value", Range(0, 255)) = 0
+ //[IntRange] _StencilWriteMaskRef ("Stencil WriteMask Value", Range(0, 255)) = 0
+ [Enum(UnityEngine.Rendering.StencilOp)] _StencilPassOp("Stencil Pass Op", Float) = 0
+ [Enum(UnityEngine.Rendering.StencilOp)] _StencilFailOp("Stencil Fail Op", Float) = 0
+ [Enum(UnityEngine.Rendering.StencilOp)] _StencilZFailOp("Stencil ZFail Op", Float) = 0
+ [Enum(UnityEngine.Rendering.CompareFunction)] _StencilCompareFunction("Stencil Compare Function", Float) = 8
+ [HideInInspector] m_end_StencilPassOptions("Stencil", Float) = 0
+
+ [HideInInspector] m_start_debugOptions("Debug", Float) = 0
+ [Toggle(_COLOROVERLAY_ON)]_DebugDisplayDebug("Display Debug Info", Float) = 0
+ [Enum(Off, 0, Vertex Normal, 1, Pixel Normal, 2, Tangent, 3, Binormal, 4)] _DebugMeshData("Mesh Data", Int) = 0
+ [Enum(Off, 0, Attenuation, 1, Direct Lighting, 2, Indirect Lighting, 3, light Map, 4, Ramped Light Map, 5, Final Lighting, 6)] _DebugLightingData("Lighting Data", Int) = 0
+ [Enum(Off, 0, finalSpecular, 1, tangentDirectionMap, 2, shiftTexture, 3)] _DebugSpecularData("Specular Data", Int) = 0
+ [Enum(Off, 0, View Dir, 1, Tangent View Dir, 2, Forward Dir, 3, WorldPos, 4, View Dot Normal, 5)] _DebugCameraData("Camera Data", Int) = 0
+ [HideInInspector] m_end_debugOptions("Debug", Float) = 0
+ }
+
+ CustomEditor "Thry.ShaderEditor"
+ SubShader{
+ Tags { "RenderType" = "Opaque" }
+ LOD 200
+
+ CGPROGRAM
+ // Physically based Standard lighting model, and enable shadows on all light types
+ #pragma surface surf Standard fullforwardshadows
+
+ // Use shader model 3.0 target, to get nicer looking lighting
+ #pragma target 3.0
+
+ sampler2D _MainTex;
+
+ struct Input {
+ float2 uv_MainTex;
+ };
+
+ half _Glossiness;
+ half _Metallic;
+ fixed4 _Color;
+
+ // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
+ // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
+ // #pragma instancing_options assumeuniformscaling
+ UNITY_INSTANCING_BUFFER_START(Props)
+ // put more per-instance properties here
+ UNITY_INSTANCING_BUFFER_END(Props)
+
+ void surf(Input IN, inout SurfaceOutputStandard o) {
+ // Albedo comes from a texture tinted by color
+ fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
+ o.Albedo = c.rgb;
+ // Metallic and smoothness come from slider variables
+ o.Metallic = _Metallic;
+ o.Smoothness = _Glossiness;
+ o.Alpha = c.a;
+ }
+ ENDCG
+ }
+ FallBack "Diffuse"
+}
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/Example1.shader.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/Example1.shader.meta
new file mode 100644
index 00000000..3bfcf026
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/Example1.shader.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 8a38f752c293b5548b674637c2a6f99a
+ShaderImporter:
+ externalObjects: {}
+ defaultTextures: []
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/Example2.shader b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/Example2.shader
new file mode 100644
index 00000000..6cdcb28f
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/Example2.shader
@@ -0,0 +1,435 @@
+Shader "Thry/Example 2"
+{
+ Properties
+ {
+ [HideInInspector] shader_is_using_thry_editor("", Float)=0
+ [HideInInspector] shader_master_label("<color=#ff0000ff>Example 2</color>", Float) = 0
+ [HideInInspector] shader_presets("ThryPresetsExample", Float) = 0
+ [HideInInspector] shader_properties_label_file("ThryLabelExample", Float) = 0
+
+ [HideInInspector] footer_website("", Float) = 0
+ [HideInInspector] footer_github("", Float) = 0
+
+
+ shader_properties_locale("locale::locale--{file_name:thry_locale_example}", Float) = 0
+ [Enum(Cutout,0,Transparent,1)]variant_selector("Variant--{on_value_actions:[{value:0,actions:[{type:SET_PROPERTY,data:_ZWrite=1},{type:SET_SHADER,data:Thry/Example 1}]},{value:1,actions:[{type:SET_PROPERTY,data:_ZWrite=0},{type:SET_SHADER,data:Thry/Example 2}]}]}",Float) = 0
+
+ [HideInInspector] m_mainOptions("Main", Float) = 0
+ _Color("Color & Alpha", Color) = (1, 1, 1, 1)
+ [Helpbox]_HelpboxForSomething("Alpha is controlled in the color", Float) = 1
+ _Saturation("Saturation", Range(-1, 1)) = 0
+ _MainVertexColoring("Use Vertex Color", Range(0,1)) = 0
+ _MainEmissionStrength("Basic Emission", Range(0, 20)) = 0
+ [Curve]_MainTex("Texture", 2D) = "white" { }
+ [PanningTexture][Normal]_BumpMap("Normal Map", 2D) = "bump" { }
+ [Enum(UV0, 0, UV1, 1, UV2, 2, UV3, 3)] _BumpMapUV("Normal UV#", Int) = 0
+ [HideInInspector][Vector2]_MainNormalPan("Panning", Vector) = (0, 0, 0, 0)
+ _BumpScale("Normal Intensity", Range(0, 10)) = 1
+ _AlphaMask("Alpha Mask", 2D) = "white" { }
+ [Vector2]_GlobalPanSpeed("Global Pan Speed", Vector) = (0, 0, 0, 0)
+
+ [HideInInspector] m_start_Alpha("Alpha Options--{altClick:{type:URL,data:https://thryrallo.de}}", Float) = 0
+ _Clip("Alpha Cuttoff", Range(0, 1.001)) = 0.5
+ [Toggle(_)]_ForceOpaque("Force Opaque", Float) = 0
+ [Toggle(_)]_MainAlphaToCoverage("Alpha To Coverage", Float) = 1
+ _MainMipScale("Mip Level Alpha Scale", Range(0, 1)) = 0.25
+ [HideInInspector] m_end_Alpha("Alpha Options", Float) = 0
+
+ [HideInInspector] m_start_DetailOptions("Details", Float) = 0
+ _DetailMask("Detail Mask (R:Texture, G:Normal)", 2D) = "white" { }
+ [PanningTexture]_DetailTex("Detail Texture", 2D) = "gray" { }
+ [HideInInspector][Vector2]_DetailTexturePan("Panning", Vector) = (0, 0, 0, 0)
+ [Enum(UV0, 0, UV1, 1, UV2, 2, UV3, 3)] _DetailTexUV("Detail Tex UV#", Int) = 0
+ _DetailTexIntensity("Detail Tex Intensity", Range(0, 10)) = 1
+ _DetailBrightness("Detail Brightness:", Range(0, 2)) = 1
+ _DetailTint("Detail Tint", Color) = (1, 1, 1)
+ [Normal]_DetailNormalMap("Detail Normal", 2D) = "bump" { }
+ [Enum(UV0, 0, UV1, 1, UV2, 2, UV3, 3)] _DetailNormalUV("Detail Normal UV#", Int) = 0
+ _DetailNormalMapScale("Detail Normal Intensity", Range(0, 10)) = 1
+ [HideInInspector][Vector2]_MainDetailNormalPan("Panning", Vector) = (0, 0, 0, 0)
+ [HideInInspector] m_end_DetailOptions("Details", Float) = 0
+
+ [HideInInspector] m_lightingOptions("Lighting Options", Float) = 0
+ [HideInInspector] m_start_Lighting("Light and Shadow", Float) = 0
+ [Toggle(_NORMALMAP)]_EnableLighting("Enable Lighting", Float) = 1
+ [HideInInspector] g_start_l("", Int) = 0
+ [Enum(Natural, 0, Controlled, 1, Standardish, 2)] _LightingType("Lighting Type", Int) = 1
+ [Gradient]_ToonRamp("Lighting Ramp", 2D) = "white" { }
+ _LightingShadowMask("Shadow Mask (R)", 2D) = "white" { }
+ _ShadowStrength("Shadow Strength", Range(0, 1)) = .2
+ _ShadowOffset("Shadow Offset", Range(-1, 1)) = 0
+ _AOMap("AO Map", 2D) = "white" { }
+ [Enum(UV0, 0, UV1, 1, UV2, 2, UV3, 3)] _LightingAOUV("AO Map UV#", Int) = 0
+ _AOStrength("AO Strength", Range(0, 1)) = 1
+ _LightingMinLightBrightness("Min Brightness", Range(0,1)) = 0
+ [HideInInspector] m_start_lightingStandard("Standardish Settings", Float) = 0
+ _LightingStandardSmoothness("Smoothness", Range(0, 1)) = 0
+ [HideInInspector] m_end_lightingStandard("Standardish Settings", Float) = 0
+ [HideInInspector] m_start_lightingAdvanced("Advanced", Float) = 0
+ _LightingIndirectContribution("Indirect Contribution", Range(0, 1)) = .25
+ _AdditiveSoftness("Additive Softness", Range(0, 0.5)) = 0.005
+ _AdditiveOffset("Additive Offset", Range(-0.5, 0.5)) = 0
+ _LightingAdditiveIntensity("Additive Intensity", Range(0,1)) = 1
+ _AttenuationMultiplier("Attenuation", Range(0, 1)) = 0
+ [HideInInspector] m_end_lightingAdvanced("Advanced", Float) = 0
+ [HideInInspector] m_start_lightingBeta("Beta", Float) = 0
+ [Toggle(_)]_LightingStandardControlsToon("Standard Lighting Controls Toon Ramp", Float) = 0
+ [HideInInspector] m_end_lightingBeta("Beta", Float) = 0
+ [HideInInspector] g_end_l("", Int) = 0
+ [HideInInspector] m_end_Lighting("Light and Shadow", Float) = 0
+
+ [HideInInspector] m_start_subsurface("Subsurface Scattering", Float) = 0
+ [Toggle(_TERRAIN_NORMAL_MAP)]_EnableSSS("Enable Subsurface Scattering", Float) = 0
+ _SSSColor("Subsurface Color", Color) = (1, 1, 1, 1)
+ _SSSThicknessMap("Thickness Map", 2D) = "black" { }
+ _SSSThicknessMod("Thickness mod", Range(-1, 1)) = 0
+ _SSSSCale("Light Strength", Range(0, 1)) = 0
+ _SSSPower("Light Spread", Range(1, 100)) = 1
+ _SSSDistortion("Light Distortion", Range(0, 1)) = 0
+ [HideInInspector] m_end_subsurface("Subsurface Scattering", Float) = 0
+
+ [HideInInspector] m_start_rimLightOptions("Rim Lighting", Float) = 0
+ [Toggle(_GLOSSYREFLECTIONS_OFF)]_EnableRimLighting("Enable Rim Lighting", Float) = 0
+ [Toggle(_)]_RimLightingInvert("Invert Rim Lighting", Float) = 0
+ _RimLightColor("Rim Color", Color) = (1, 1, 1, 1)
+ _RimWidth("Rim Width", Range(0, 1)) = 0.8
+ _RimSharpness("Rim Sharpness", Range(0, 1)) = .25
+ _RimStrength("Rim Emission", Range(0, 20)) = 0
+ _RimBrighten("Rim Color Brighten", Range(0, 3)) = 0
+ _RimLightColorBias("Rim Color Bias", Range(0, 1)) = 0
+ [PanningTexture]_RimTex("Rim Texture", 2D) = "white" { }
+ _RimMask("Rim Mask", 2D) = "white" { }
+ [HideInInspector][Vector2]_RimTexPanSpeed("Panning", Vector) = (0, 0, 0, 0)
+ [HideInInspector] m_start_reflectionRim("Environmental Rim", Float) = 0
+ [Toggle(_)]_EnableEnvironmentalRim("Enable Environmental Rim", Float) = 0
+ _RimEnviroMask("Mask", 2D) = "white" { }
+ _RimEnviroBlur("Blur", Range(0, 1)) = 0.7
+ _RimEnviroWidth("Rim Width", Range(0, 1)) = 0.45
+ _RimEnviroSharpness("Rim Sharpness", Range(0, 1)) = 0
+ _RimEnviroMinBrightness("Min Brightness Threshold", Range(0, 2)) = 0
+ [HideInInspector] m_end_reflectionRim("Environmental Rim", Float) = 0
+ [HideInInspector] m_start_rimWidthNoise("Width Noise", Float) = 0
+ [PanningTexture]_RimWidthNoiseTexture("Rim Width Noise", 2D) = "black" { }
+ _RimWidthNoiseStrength("Intensity", Range(0, 1)) = 0.1
+ [HideInInspector][Vector2]_RimWidthNoisePan("Panning", Vector) = (0, 0, 0, 0)
+ [HideInInspector] m_end_rimWidthNoise("Width Noise", Float) = 0
+ [HideInInspector] m_start_ShadowMix("Shadow Mix", Float) = 0
+ _ShadowMix("Shadow Mix In", Range(0, 1)) = 0
+ _ShadowMixThreshold("Shadow Mix Threshold", Range(0, 1)) = .5
+ _ShadowMixWidthMod("Shadow Mix Width Mod", Range(0, 10)) = .5
+ [HideInInspector] m_end_ShadowMix("Shadow Mix", Float) = 0
+ [HideInInspector] m_end_rimLightOptions("Rim Lighting", Float) = 0
+
+ [HideInInspector] m_start_bakedLighting("Baked Lighting", Float) = 0
+ _GIEmissionMultiplier("GI Emission Multiplier", Float) = 1
+ [HideInInspector] DSGI("DSGI", Float) = 0 //add this property for double sided illumination settings to be shown
+ [HideInInspector] LightmapFlags("Lightmap Flags", Float) = 0 //add this property for lightmap flags settings to be shown
+ [HideInInspector] m_end_bakedLighting("Baked Lighting", Float) = 0
+
+ [HideInInspector] m_reflectionOptions("Reflections", Float) = 0
+ [HideInInspector] m_start_Metallic("Metallics", Float) = 0
+ [Toggle(_METALLICGLOSSMAP)]_EnableMetallic("Enable Metallics", Float) = 0
+ _CubeMap("Baked CubeMap", Cube) = "" { }
+ [Toggle(_)]_SampleWorld("Force Baked Cubemap", Range(0, 1)) = 0
+ _MetalReflectionTint("Reflection Tint", Color) = (1, 1, 1)
+ _MetallicMask("Metallic Mask", 2D) = "white" { }
+ _Metallic("Metallic", Range(0, 1)) = 0
+ _SmoothnessMask("Smoothness Map", 2D) = "white" { }
+ [Toggle(_)]_InvertSmoothness("Invert Smoothness Map", Range(0, 1)) = 0
+ _Smoothness("Smoothness", Range(0, 1)) = 0
+ [HideInInspector] m_end_Metallic("Metallics", Float) = 0
+
+ [HideInInspector] m_start_clearCoat("Clear Coat", Float) = 0
+ [Toggle(_COLORCOLOR_ON)]_EnableClearCoat("Enable Clear Coat", Float) = 0
+ [Enum(Vertex, 0, Pixel, 1)] _ClearCoatNormalToUse("What Normal?", Int) = 0
+ _ClearCoatCubeMap("Baked CubeMap", Cube) = "" { }
+ [Toggle(_)]_ClearCoatSampleWorld("Force Baked Cubemap", Range(0, 1)) = 0
+ _ClearCoatTint("Reflection Tint", Color) = (1, 1, 1)
+ _ClearCoatMask("Mask", 2D) = "white" { }
+ _ClearCoat("Clear Coat", Range(0, 1)) = 1
+ _ClearCoatSmoothnessMask("Smoothness Map", 2D) = "white" { }
+ [Toggle(_)]_ClearCoatInvertSmoothness("Invert Smoothness Map", Range(0, 1)) = 0
+ _ClearCoatSmoothness("Smoothness", Range(0, 1)) = 0
+ [Toggle(_)]_ClearCoatForceLighting("Force Lighting", Float) = 0
+ [HideInInspector] m_end_clearCoat("Clear Coat", Float) = 0
+
+ [HideInInspector] m_start_matcap("Matcap / Sphere Textures", Float) = 0
+ [Toggle(_COLORADDSUBDIFF_ON)]_MatcapEnable("Enable Matcap", Float) = 0
+ _MatcapColor("Color", Color) = (1, 1, 1, 1)
+ [TextureNoSO]_Matcap("Matcap", 2D) = "white" { }
+ _MatcapBorder("Border", Range(0, .5)) = 0.43
+ _MatcapMask("Mask", 2D) = "white" { }
+ _MatcapIntensity("Intensity", Range(0, 5)) = 1
+ _MatcapLightMask("Hide in Shadow", Range(0, 1)) = 0
+ _MatcapReplace("Replace With Matcap", Range(0, 1)) = 1
+ _MatcapMultiply("Multiply Matcap", Range(0, 1)) = 0
+ _MatcapAdd("Add Matcap", Range(0, 1)) = 0
+ [Enum(Vertex, 0, Pixel, 1)] _MatcapNormal("Normal to use", Int) = 1
+ [HideInInspector] m_end_matcap("Matcap", Float) = 0
+ [HideInInspector] m_start_Matcap2("Matcap 2", Float) = 0
+ [Toggle(_)]_Matcap2Enable("Enable Matcap 2", Float) = 0
+ _Matcap2Color("Color", Color) = (1, 1, 1, 1)
+ [TextureNoSO]_Matcap2("Matcap", 2D) = "white" { }
+ _Matcap2Border("Border", Range(0, .5)) = 0.43
+ _Matcap2Mask("Mask", 2D) = "white" { }
+ _Matcap2Intensity("Intensity", Range(0, 5)) = 1
+ _Matcap2LightMask("Hide in Shadow", Range(0, 1)) = 0
+ _Matcap2Replace("Replace With Matcap", Range(0, 1)) = 0
+ _Matcap2Multiply("Multiply Matcap", Range(0, 1)) = 0
+ _Matcap2Add("Add Matcap", Range(0, 1)) = 0
+ [Enum(Vertex, 0, Pixel, 1)] _Matcap2Normal("Normal to use", Int) = 1
+ [HideInInspector] m_end_Matcap2("Matcap 2", Float) = 0
+
+ [HideInInspector] m_start_specular("Specular Reflections", Float) = 0
+ [Toggle(_SPECGLOSSMAP)]_EnableSpecular("Enable Specular", Float) = 0
+ [Enum(Realistic, 1, Toon, 2, Anisotropic, 3)] _SpecularType("Specular Type", Int) = 1
+ _SpecularMinLightBrightness("Min Light Brightness", Range(0, 1)) = 0
+ _SpecularTint("Specular Tint", Color) = (.2, .2, .2, 1)
+ _SpecularMixAlbedoIntoTint("Mix Material Color Into Tint", Range(0, 1)) = 0
+ _SpecularSmoothness("Smoothness", Range(-2, 1)) = .75
+ _SpecularMap("Specular Map", 2D) = "white" { }
+ [Toggle(_)]_SpecularInvertSmoothness("Invert Smoothness", Float) = 0
+ _SpecularMask("Specular Mask", 2D) = "white" { }
+ [Enum(Alpha, 0, Grayscale, 1)] _SmoothnessFrom("Smoothness From", Int) = 1
+ [HideInInspector] m_start_SpecularToon("Toon", Float) = 0
+ [MultiSlider]_SpecularToonInnerOuter("Inner/Outer Edge", Vector) = (0.25, 0.3, 0, 1)
+ [HideInInspector] m_end_SpecularToon("Toon", Float) = 0
+ [HideInInspector] m_start_Anisotropic("Anisotropic", Float) = 0
+ [Enum(Tangent, 0, Bitangent, 1)] _SpecWhatTangent("(Bi)Tangent?", Int) = 0
+ _AnisoSpec1Alpha("Spec1 Alpha", Range(0, 1)) = 1
+ _AnisoSpec2Alpha("Spec2 Alpha", Range(0, 1)) = 1
+ //_Spec1Offset ("Spec1 Offset", Float) = 0
+ //_Spec1JitterStrength ("Spec1 Jitter Strength", Float) = 1.0
+ _Spec2Smoothness("Spec2 Smoothness", Range(0, 1)) = 0
+ //_Spec2Offset ("Spec2 Offset", Float) = 0
+ //_Spec2JitterStrength ("Spec2 Jitter Strength", Float) = 1.0
+ [Toggle(_)]_AnisoUseTangentMap("Use Directional Map?", Float) = 0
+ _AnisoTangentMap("Anisotropic Directional Map", 2D) = "bump" { }
+ //_ShiftTexture ("Shift Texture", 2D) = "black" { }
+ [HideInInspector] m_end_Anisotropic("Anisotropic", Float) = 0
+ [HideInInspector] m_end_specular("Specular Reflections", Float) = 0
+
+ [HideInInspector] m_Special_Effects("Special Effects", Float) = 0
+ [HideInInspector] m_start_emissionOptions("Emission / Glow", Float) = 0
+ [Toggle(_EMISSION)]_EnableEmission("Enable Emission", Float) = 0
+ [Enum(UV0, 0, UV1, 1, UV2, 2, UV3, 3)] _EmissionUV("Emission UV#", Int) = 0
+ [HDR]_EmissionColor("Emission Color", Color) = (1, 1, 1, 1)
+ [PanningTexture]_EmissionMap("Emission Map", 2D) = "white" { }
+ [PanningTexture]_EmissionMask("Emission Mask", 2D) = "white" { }
+ [HideInInspector][Vector2]_EmissionMapPan("Panning", Vector) = (0, 0, 0, 0)
+ [HideInInspector][Vector2]_EmissionMaskPan("Panning", Vector) = (0, 0, 0, 0)
+ _EmissionStrength("Emission Strength", Range(0, 20)) = 0
+ // Inward out emission
+ [HideInInspector] m_start_CenterOutEmission("Center Out Emission", Float) = 0
+ [Toggle(_)]_EmissionCenterOutEnabled("Enable Center Out", Float) = 0
+ _EmissionCenterOutSpeed("Flow Speed", Float) = 5
+ [HideInInspector] m_end_CenterOutEmission("inward out emission", Float) = 0
+ //Glow in the dark Emission
+ [HideInInspector] m_start_glowInDarkEmissionOptions("Glow In The Dark Emission (Requires Lighting Enabled)", Float) = 0
+ [Toggle(_)]_EnableGITDEmission("Enable Glow In The Dark", Float) = 0
+ [Enum(World, 0, Mesh, 1)] _GITDEWorldOrMesh("Lighting Type", Int) = 0
+ _GITDEMinEmissionMultiplier("Min Emission Multiplier", Range(0, 1)) = 1
+ _GITDEMaxEmissionMultiplier("Max Emission Multiplier", Range(0, 1)) = 0
+ _GITDEMinLight("Min Lighting", Range(0, 1)) = 0
+ _GITDEMaxLight("Max Lighting", Range(0, 1)) = 1
+ [HideInInspector] m_end_glowInDarkEmissionOptions("Glow In The Dark Emission (Requires Lighting Enabled)", Float) = 0
+
+ [HideInInspector] m_start_blinkingEmissionOptions("Blinking Emission", Float) = 0
+ _EmissiveBlink_Min("Emissive Blink Min", Float) = 1
+ _EmissiveBlink_Max("Emissive Blink Max", Float) = 1
+ _EmissiveBlink_Velocity("Emissive Blink Velocity", Float) = 4
+ [HideInInspector] m_end_blinkingEmissionOptions("Blinking Emission", Float) = 0
+
+ [HideInInspector] m_start_scrollingEmissionOptions("Scrolling Emission", Float) = 0
+ [Toggle(_)] _ScrollingEmission("Enable Scrolling Emission", Float) = 0
+ _EmissiveScroll_Direction("Emissive Scroll Direction", Vector) = (0, -10, 0, 0)
+ _EmissiveScroll_Width("Emissive Scroll Width", Float) = 10
+ _EmissiveScroll_Velocity("Emissive Scroll Velocity", Float) = 10
+ _EmissiveScroll_Interval("Emissive Scroll Interval", Float) = 20
+ [HideInInspector] m_end_scrollingEmissionOptions("Scrolling Emission", Float) = 0
+ [HideInInspector] m_end_emissionOptions("Emission / Glow", Float) = 0
+
+ [HideInInspector] m_start_flipBook("Flipbook", Float) = 0
+ [Toggle(_FLIPBOOK_BLENDING)]_EnableFlipbook("Enable Flipbook", Float) = 0
+ [Enum(UV0, 0, UV1, 1, UV2, 2, UV3, 3)] _FlipbookUV("Flipbook UV#", Int) = 0
+ [TextureArray]_FlipbookTexArray("Texture Array--{reference_property:_FlipbookTotalFrames}", 2DArray) = "" { }
+ _FlipbookColor("Color & alpha", Color) = (1, 1, 1, 1)
+ _FlipbookTotalFrames("Total Frames", Int) = 1
+ _FlipbookFPS("FPS", Float) = 30.0
+ _FlipbookScaleOffset("Scale | Offset", Vector) = (1, 1, 0, 0)
+ [Toggle(_)]_FlipbookTiled("Tiled?", Float) = 0
+ _FlipbookEmissionStrength("Emission Strength", Range(0, 20)) = 0
+ _FlipbookRotation("Rotation", Range(0, 360)) = 0
+ _FlipbookReplace("Replace", Range(0, 1)) = 1
+ _FlipbookMultiply("Multiply", Range(0, 1)) = 0
+ _FlipbookAdd("Add", Range(0, 1)) = 0
+ [HideInInspector] m_start_manualFlipbookControl("Manual Control", Float) = 0
+ _FlipbookCurrentFrame("Current Frame", Float) = -1
+ [HideInInspector] m_end_manualFlipbookControl("Manual Control", Float) = 0
+ [HideInInspector] m_end_flipBook("Flipbook", Float) = 0
+
+ [HideInInspector] m_start_dissolve("Dissolve", Float) = 0
+ [Toggle(_ALPHABLEND_ON)]_EnableDissolve("Enable Dissolve", Float) = 0
+ [Enum(Basic, 1, Point2Point, 2)] _DissolveType("Dissolve Type", Int) = 1
+ _DissolveEdgeWidth("Edge Width", Range(0, .5)) = 0.025
+ _DissolveEdgeHardness("Edge Hardness", Range(0, 1)) = 0.5
+ _DissolveEdgeColor("Edge Color", Color) = (1, 1, 1, 1)
+ [Gradient]_DissolveEdgeGradient("Edge Gradient", 2D) = "white" { }
+ _DissolveEdgeEmission("Edge Emission", Range(0, 20)) = 0
+ _DissolveTextureColor("Dissolve to Color", Color) = (1, 1, 1, 1)
+ [PanningTexture]_DissolveToTexture("Dissolve to Texture", 2D) = "white" { }
+ _DissolveToEmissionStrength("Dissolve to Emission Strength", Range(0, 20)) = 0
+ [HideInInspector][Vector2]_DissolveToPanning("Panning", Vector) = (0, 0, 0, 0)
+ [PanningTexture]_DissolveNoiseTexture("Dissolve Noise", 2D) = "white" { }
+ [Toggle(_)]_DissolveInvertNoise("Invert Noise", Float) = 0
+ [PanningTexture]_DissolveDetailNoise("Dissolve Detail Noise", 2D) = "black" { }
+ [Toggle(_)]_DissolveInvertDetailNoise("Invert Detail Noise", Float) = 0
+ _DissolveDetailStrength("Dissolve Detail Strength", Range(0, 1)) = 0.1
+ [HideInInspector][Vector2]_DissolveNoisePan("Panning", Vector) = (0, 0, 0, 0)
+ [HideInInspector][Vector2]_DissolveDetailPan("Panning", Vector) = (0, 0, 0, 0)
+ _DissolveAlpha("Dissolve Alpha", Range(0, 1)) = 0
+ _DissolveMask("Dissolve Mask", 2D) = "white" { }
+ [Toggle(_)]_ContinuousDissolve("Continuous Dissolve Speed", Float) = 0
+ [HideInInspector] m_start_pointToPoint("point to point", Float) = 0
+ [Enum(Local, 0, World, 1)] _DissolveP2PWorldLocal("World/Local", Int) = 0
+ _DissolveP2PEdgeLength("Edge Length", Float) = 0.1
+ [Vector3]_DissolveStartPoint("Start Point", Vector) = (0, -1, 0, 0)
+ [Vector3]_DissolveEndPoint("End Point", Vector) = (0, 1, 0, 0)
+ [HideInInspector] m_end_pointToPoint("Point To Point", Float) = 0
+ [HideInInspector] m_end_dissolve("Dissolve", Float) = 0
+
+ [HideInInspector] m_start_panosphereOptions("Panosphere / Cubemaps", Float) = 0
+ [Toggle(_DETAIL_MULX2)]_PanoToggle("Enable Panosphere", Float) = 0
+ _PanosphereColor("Color", Color) = (1, 1, 1, 1)
+ _PanosphereTexture("Texture", 2D) = "white" { }
+ _PanoMapTexture("Mask", 2D) = "white" { }
+ _PanoEmission("Emission Strength", Range(0, 10)) = 0
+ _PanoBlend("Alpha", Range(0, 1)) = 0
+ [Vector3]_PanospherePan("Pan Speed", Vector) = (0, 0, 0, 0)
+ [Toggle(_)]_PanoCubeMapToggle("Use Cubemap", Float) = 0
+ [TextureNoSO]_PanoCubeMap("CubeMap", Cube) = "" { }
+ [HideInInspector] m_end_panosphereOptions("Panosphere / Cubemaps", Float) = 0
+
+ [HideInInspector] m_start_mirrorOptions("Mirror", Float) = 0
+ [Toggle(_REQUIRE_UV2)]_EnableMirrorOptions("Enable Mirror Options", Float) = 0
+ [Enum(ShowInBoth, 0, ShowOnlyInMirror, 1, DontShowInMirror, 2)] _Mirror("Show in mirror", Int) = 0
+ [Toggle(_)]_EnableMirrorTexture("Enable Mirror Texture", Float) = 0
+ _MirrorTexture("Mirror Tex", 2D) = "white" { }
+ [HideInInspector] m_end_mirrorOptions("Mirror", Float) = 0
+
+ [HideInInspector] m_start_distanceFade("Distance Fade", Float) = 0
+ _MainMinAlpha("Minimum Alpha", Range(0, 1)) = 0
+ _MainFadeTexture("Fade Map", 2D) = "white" { }
+ [Vector2]_MainDistanceFade("Distance Fade X to Y", Vector) = (0, 0, 0, 0)
+ [HideInInspector] m_end_distanceFade("Distance Fade", Float) = 0
+
+ [HideInInspector] m_start_angularFade("Angular Fade", Float) = 0
+ [Toggle(_SUNDISK_NONE)]_EnableRandom("Enable Angular Fade", Float) = 0
+ [Enum(Camera Face Model, 0, Model Face Camera, 1, Face Each Other, 2)] _AngleType("Angle Type", Int) = 0
+ [Enum(Model, 0, Vertex, 1)] _AngleCompareTo("Model or Vert Positon", Int) = 0
+ [Vector3]_AngleForwardDirection("Forward Direction", Vector) = (0, 0, 1, 0)
+ _CameraAngleMin("Camera Angle Min", Range(0, 180)) = 45
+ _CameraAngleMax("Camera Angle Max", Range(0, 180)) = 90
+ _ModelAngleMin("Model Angle Min", Range(0, 180)) = 45
+ _ModelAngleMax("Model Angle Max", Range(0, 180)) = 90
+ _AngleMinAlpha("Min Alpha", Range(0, 1)) = 0
+ [HideInInspector] m_end_angularFade("Angular Fade", Float) = 0
+ // End Special Effects
+
+ [HideInInspector] m_parallaxMap("Parallax", Float) = 0
+ [Toggle(_PARALLAXMAP)]_ParallaxMap("Enable Parallax FX", Float) = 0
+ [Toggle(_)]_ParallaxHeightMapEnabled("Enable Parallax Height", Float) = 0
+ [Toggle(_)]_ParallaxInternalMapEnabled("Enable Parallax Internal", Float) = 0
+ [HideInInspector] m_start_parallaxHeightmap("Heightmap", Float) = 0
+ _ParallaxHeightMap("Height Map", 2D) = "black" { }
+ _ParallaxStrength("Parallax Strength", Range(0, 1)) = 0
+ [HideInInspector] m_end_parallaxHeightmap("Heightmap", Float) = 0
+ [HideInInspector] m_start_parallaxInternal("Internal", Float) = 0
+ [Enum(Basic, 0, HeightMap, 1)] _ParallaxInternalHeightmapMode("Parallax Mode", Int) = 0
+ [Toggle(_)]_ParallaxInternalHeightFromAlpha("HeightFromAlpha", Float) = 0
+ _ParallaxInternalMap("Internal Map", 2D) = "black" { }
+ _ParallaxInternalIterations("Parallax Internal Iterations", Range(1, 50)) = 1
+ _ParallaxInternalMinDepth("Min Depth", Float) = 0
+ _ParallaxInternalMaxDepth("Max Depth", Float) = 1
+ _ParallaxInternalMinFade("Min Depth Brightness", Range(0, 5)) = 0
+ _ParallaxInternalMaxFade("Max Depth Brightness", Range(0, 5)) = 1
+ _ParallaxInternalMinColor("Min Depth Color", Color) = (1, 1, 1, 1)
+ _ParallaxInternalMaxColor("Max Depth Color", Color) = (1, 1, 1, 1)
+ [Vector2]_ParallaxInternalPanSpeed("Pan Speed", Vector) = (0, 0, 0, 0)
+ [Vector2]_ParallaxInternalPanDepthSpeed("Per Level Speed Multiplier", Vector) = (0, 0, 0, 0)
+ [HideInInspector] m_end_parallaxInternal("Internal", Float) = 0
+ [HideInInspector] m_start_parallaxAdvanced("Advanced", Float) = 0
+ _ParallaxBias("Parallax Bias (0.42)", Float) = 0.42
+ [HideInInspector] m_end_parallaxAdvanced("Advanced", Float) = 0
+
+ [HideInInspector] m_renderingOptions("Rendering Options", Float) = 0
+ [Enum(UnityEngine.Rendering.CullMode)] _Cull("Cull", Float) = 2
+ [Enum(UnityEngine.Rendering.CompareFunction)] _ZTest("ZTest", Float) = 4
+ [Enum(UnityEngine.Rendering.BlendMode)] _SourceBlend("Source Blend", Float) = 5
+ [Enum(UnityEngine.Rendering.BlendMode)] _DestinationBlend("Destination Blend", Float) = 10
+ [Enum(Off, 0, On, 1)] _ZWrite("ZWrite", Int) = 1
+ _ZBias("ZBias", Float) = 0.0
+ [Toggle(_)]_IgnoreFog("Ignore Fog", Float) = 0
+ //[HideInInspector] Instancing ("Instancing", Float) = 0 //add this property for instancing variants settings to be shown
+
+ [HideInInspector] m_start_StencilPassOptions("Stencil", Float) = 0
+ [IntRange] _StencilRef("Stencil Reference Value", Range(0, 255)) = 0
+ //[IntRange] _StencilReadMaskRef ("Stencil ReadMask Value", Range(0, 255)) = 0
+ //[IntRange] _StencilWriteMaskRef ("Stencil WriteMask Value", Range(0, 255)) = 0
+ [Enum(UnityEngine.Rendering.StencilOp)] _StencilPassOp("Stencil Pass Op", Float) = 0
+ [Enum(UnityEngine.Rendering.StencilOp)] _StencilFailOp("Stencil Fail Op", Float) = 0
+ [Enum(UnityEngine.Rendering.StencilOp)] _StencilZFailOp("Stencil ZFail Op", Float) = 0
+ [Enum(UnityEngine.Rendering.CompareFunction)] _StencilCompareFunction("Stencil Compare Function", Float) = 8
+ [HideInInspector] m_end_StencilPassOptions("Stencil", Float) = 0
+
+ [HideInInspector] m_start_debugOptions("Debug", Float) = 0
+ [Toggle(_COLOROVERLAY_ON)]_DebugDisplayDebug("Display Debug Info", Float) = 0
+ [Enum(Off, 0, Vertex Normal, 1, Pixel Normal, 2, Tangent, 3, Binormal, 4)] _DebugMeshData("Mesh Data", Int) = 0
+ [Enum(Off, 0, Attenuation, 1, Direct Lighting, 2, Indirect Lighting, 3, light Map, 4, Ramped Light Map, 5, Final Lighting, 6)] _DebugLightingData("Lighting Data", Int) = 0
+ [Enum(Off, 0, finalSpecular, 1, tangentDirectionMap, 2, shiftTexture, 3)] _DebugSpecularData("Specular Data", Int) = 0
+ [Enum(Off, 0, View Dir, 1, Tangent View Dir, 2, Forward Dir, 3, WorldPos, 4, View Dot Normal, 5)] _DebugCameraData("Camera Data", Int) = 0
+ [HideInInspector] m_end_debugOptions("Debug", Float) = 0
+ }
+
+ CustomEditor "Thry.ShaderEditor"
+ SubShader{
+ Tags { "RenderType" = "Transparent" "Queue"="Transparent" }
+ LOD 200
+
+ CGPROGRAM
+ // Physically based Standard lighting model, and enable shadows on all light types
+ #pragma surface surf Standard fullforwardshadows
+
+ // Use shader model 3.0 target, to get nicer looking lighting
+ #pragma target 3.0
+
+ sampler2D _MainTex;
+
+ struct Input {
+ float2 uv_MainTex;
+ };
+
+ half _Glossiness;
+ half _Metallic;
+ fixed4 _Color;
+
+ // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
+ // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
+ // #pragma instancing_options assumeuniformscaling
+ UNITY_INSTANCING_BUFFER_START(Props)
+ // put more per-instance properties here
+ UNITY_INSTANCING_BUFFER_END(Props)
+
+ void surf(Input IN, inout SurfaceOutputStandard o) {
+ // Albedo comes from a texture tinted by color
+ fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
+ o.Albedo = c.rgb;
+ // Metallic and smoothness come from slider variables
+ o.Metallic = _Metallic;
+ o.Smoothness = _Glossiness;
+ o.Alpha = c.a;
+ }
+ ENDCG
+ }
+ FallBack "Diffuse"
+}
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/Example2.shader.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/Example2.shader.meta
new file mode 100644
index 00000000..148b50ff
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/Example2.shader.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 0b436e0db169bde429124bf491f28cef
+ShaderImporter:
+ externalObjects: {}
+ defaultTextures: []
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/ThryLabelExample.txt b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/ThryLabelExample.txt
new file mode 100644
index 00000000..5793b632
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/ThryLabelExample.txt
@@ -0,0 +1,6 @@
+m_start_Lighting:=Light and Shadow--{reference_property:_EnableLighting,button_right:{text:Tutorial,action:{type:URL,data:https://youtu.be/2B-EJutVjs8},hover:YouTube}}
+_EnableLighting:=Enable Lighting--{hide_in_inspector:true}
+g_start_l:=--{condition_enable:{type:PROPERTY_BOOL,data:_EnableLighting}}
+
+footer_website:={texture:{name:thry_settings_icon,height:32},action:{type:URL,data:https://www.ShaderEditor.thryrallo.de},hover:Website}
+footer_github:={text:Github,action:{type:URL,data:https://github.com/thryrallo/ShaderEditor}}
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/ThryLabelExample.txt.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/ThryLabelExample.txt.meta
new file mode 100644
index 00000000..b4b77ff4
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/ThryLabelExample.txt.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: dfe226dc787c45b4780d5cc939390f76
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/ThryPresetsExample.txt b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/ThryPresetsExample.txt
new file mode 100755
index 00000000..e69de29b
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/ThryPresetsExample.txt
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/ThryPresetsExample.txt.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/ThryPresetsExample.txt.meta
new file mode 100644
index 00000000..c77c8039
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/ThryPresetsExample.txt.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: cf52f15671610fa4a80b4ee95174e3cc
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/thry_locale_example.csv b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/thry_locale_example.csv
new file mode 100644
index 00000000..77c5cb25
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/thry_locale_example.csv
@@ -0,0 +1,3 @@
+,English,German
+MainTex,Main Texture,Haupt Texture
+locale,Language,Sprache \ No newline at end of file
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/thry_locale_example.csv.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/thry_locale_example.csv.meta
new file mode 100644
index 00000000..4ac82940
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Examples/thry_locale_example.csv.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 901c2a6413418de44ab1678f66fa3826
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/LICENSE.md b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/LICENSE.md
new file mode 100644
index 00000000..e72bfdda
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/LICENSE.md
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>. \ No newline at end of file
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/LICENSE.md.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/LICENSE.md.meta
new file mode 100644
index 00000000..b5a2eee5
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/LICENSE.md.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: eae8b588a2e3ab84e834f00087295749
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/README.md b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/README.md
new file mode 100644
index 00000000..ee0c10a1
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/README.md
@@ -0,0 +1,4 @@
+# ThryEditor
+General Unity Shader Inspector/Editor with focus on vrchat shaders
+
+# [Discord Server for all my Assets](https://discord.thryrallo.de/)
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/README.md.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/README.md.meta
new file mode 100644
index 00000000..0f8d83e4
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/README.md.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: b0a20da17637cd6449605b1e443b1d4d
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Resources.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Resources.meta
new file mode 100644
index 00000000..bb48709e
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Resources.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 728d3b48899dd344b87cae0291102c6d
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Resources/thryEditor_iconThry.png b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Resources/thryEditor_iconThry.png
new file mode 100755
index 00000000..10b30712
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Resources/thryEditor_iconThry.png
Binary files differ
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Resources/thryEditor_iconThry.png.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Resources/thryEditor_iconThry.png.meta
new file mode 100644
index 00000000..f5fecd1f
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Resources/thryEditor_iconThry.png.meta
@@ -0,0 +1,116 @@
+fileFormatVersion: 2
+guid: 693aa4c2cdc578346a196469a06ddbba
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 11
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ streamingMipmaps: 0
+ streamingMipmapsPriority: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 1
+ aniso: 1
+ mipBias: 0
+ wrapU: 0
+ wrapV: 0
+ wrapW: 0
+ nPOTScale: 1
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 0
+ textureShape: 1
+ singleChannelComponent: 0
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ applyGammaDecoding: 0
+ platformSettings:
+ - serializedVersion: 3
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 3
+ buildTarget: Standalone
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 3
+ buildTarget: Android
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ bones: []
+ spriteID:
+ internalID: 0
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ secondaryTextures: []
+ spritePackingTag:
+ pSDRemoveMatte: 0
+ pSDShowRemoveMatteOption: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Resources/thryEditor_link.png b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Resources/thryEditor_link.png
new file mode 100755
index 00000000..286d510f
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Resources/thryEditor_link.png
Binary files differ
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Resources/thryEditor_link.png.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Resources/thryEditor_link.png.meta
new file mode 100644
index 00000000..94cc20ac
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Resources/thryEditor_link.png.meta
@@ -0,0 +1,104 @@
+fileFormatVersion: 2
+guid: e85fd0a0e4e4fea46bb3fdeab5c3fb07
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 11
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ streamingMipmaps: 0
+ streamingMipmapsPriority: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: -1
+ aniso: -1
+ mipBias: -100
+ wrapU: -1
+ wrapV: -1
+ wrapW: -1
+ nPOTScale: 1
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 0
+ textureShape: 1
+ singleChannelComponent: 0
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ applyGammaDecoding: 0
+ platformSettings:
+ - serializedVersion: 3
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 3
+ buildTarget: Standalone
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ bones: []
+ spriteID:
+ internalID: 0
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ secondaryTextures: []
+ spritePackingTag:
+ pSDRemoveMatte: 0
+ pSDShowRemoveMatteOption: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Resources/thry_rect.png b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Resources/thry_rect.png
new file mode 100755
index 00000000..95323f1f
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Resources/thry_rect.png
Binary files differ
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Resources/thry_rect.png.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Resources/thry_rect.png.meta
new file mode 100644
index 00000000..606e8290
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/Resources/thry_rect.png.meta
@@ -0,0 +1,116 @@
+fileFormatVersion: 2
+guid: 2329f8696fd09a743a5baf2a5f4986af
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 11
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ streamingMipmaps: 0
+ streamingMipmapsPriority: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 1
+ aniso: 1
+ mipBias: 0
+ wrapU: 0
+ wrapV: 0
+ wrapW: 0
+ nPOTScale: 0
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 1
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 60, y: 0, z: 60, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 0
+ spriteTessellationDetail: -1
+ textureType: 0
+ textureShape: 1
+ singleChannelComponent: 0
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ applyGammaDecoding: 1
+ platformSettings:
+ - serializedVersion: 3
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 3
+ buildTarget: Standalone
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 3
+ buildTarget: Android
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 1
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ bones: []
+ spriteID: 5e97eb03825dee720800000000000000
+ internalID: 0
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ secondaryTextures: []
+ spritePackingTag:
+ pSDRemoveMatte: 0
+ pSDShowRemoveMatteOption: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/docs.html b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/docs.html
new file mode 100644
index 00000000..38ec893d
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/docs.html
@@ -0,0 +1,639 @@
+<html>
+<head>
+ <title>Thry Editor Documentation</title>
+ <style>
+ body {
+ font-family: Verdana;
+ }
+
+ table, th, td {
+ border: 1px solid black;
+ border-collapse: collapse;
+ }
+
+ th, tr, td {
+ padding: 7px;
+ }
+
+ h2 {
+ text-decoration: underline;
+ }
+ div {
+ border: 1px solid black;
+ border-collapse: collapse;
+ padding: 7px;
+ }
+ .noborder{
+ border: 0px solid black;
+ }
+ </style>
+ <head>
+ <body>
+ <h2>Editor Properties</h2>
+ <table>
+ <tr>
+ <th>Name</th>
+ <th>Effect</th>
+ <th>Required</th>
+ <th>Example</th>
+ </tr>
+ <tr>
+ <td>shader_master_label</td>
+ <td>Display name customizes the gui header</td>
+ <td>No</td>
+ <td>[HideInInspector] shader_master_label ("&lt;color =#ff0000ff>❤&lt;/color> &lt;color =#000000ff>Poiyomi Toon Shader V4.0&lt;/color> &lt;color =#ff0000ff>❤&lt;/color>", Float) = 0</td>
+ </tr>
+ <tr>
+ <td>shader_properties_label_file</td>
+ <td>If specified the editor tries to load property's display names out of this file</td>
+ <td>No</td>
+ <td>[HideInInspector] shader_properties_label_file ("poiToonLabels", Float) = 0</td>
+ </tr>
+ <tr>
+ <td>shader_properties_locale</td>
+ <td>Is used to specify a locale file and locale selector. Specify "locale::&lt;string>" anywhere to load a string from the locale file.</td>
+ <td>No</td>
+ <td>[HideInInspector] shader_properties_locale ("locale::locale--{file_name:locale_file_name}", Float) = 0</td>
+ </tr>
+ <tr>
+ <td>shader_on_swap_to</td>
+ <td>Is used to specify actions to be executed when the material is switched to this shader.</td>
+ <td>No</td>
+ <td>[HideInInspector] shader_on_swap_to ("--{actions:[{type:SET_PROPERTY,data:_ZWrite=1},{type:SET_PROPERTY,data:_CullBack=0}]}", Float) = 0</td>
+ </tr>
+ <tr>
+ <td>footer_&lt;string></td>
+ <td>Adds a footer button to the bottom of the ui. Multiple footers can be added to one shader. The displayname has to be a ButtonData object</td>
+ <td>No</td>
+ <td>
+ [HideInInspector] footer_github ("{text:Github,action:{type:URL,data:https://github.com/thryrallo/thryeditor}}", Float) = 0<br />
+ [HideInInspector] footer_discord ("{texture:{name:discord-icon,height:40},action:{type:URL,data:https://discord.gg}}", Float) = 0
+ </td>
+ </tr>
+ <tr class="noborder"><td class="noborder"></td></tr>
+ <tr>
+ <td>DSGI</td>
+ <td>add this property for double sided illumination settings to be shown</td>
+ <td>No</td>
+ <td>[HideInInspector] DSGI ("", Float) = 0</td>
+ </tr>
+ <tr>
+ <td>Instancing</td>
+ <td>add this property for instancing variants settings to be shown</td>
+ <td>No</td>
+ <td>[HideInInspector] Instancing ("", Float) = 0</td>
+ </tr>
+ <tr>
+ <td>LightmapFlags</td>
+ <td>add this property for lightmap flags settings to be shown</td>
+ <td>No</td>
+ <td>[HideInInspector] LightmapFlags ("", Float) = 0</td>
+ </tr>
+ <tr class="noborder"><td class="noborder"></td></tr>
+ <tr>
+ <td>m_&lt;string></td>
+ <td>starts a dropdown menu. all properties underneath, till the next menu is specified, are in this menu</td>
+ <td>No</td>
+ <td>[HideInInspector] m_mainOptions ("Main", Float) = 0</td>
+ </tr>
+ <tr>
+ <td>m_start_&lt;string></td>
+ <td>starts a dropdown menu. all properties underneath, till this menu's end is specified, are in this menu. use if you want to layer menus.</td>
+ <td>No</td>
+ <td>[HideInInspector] m_start_Alpha ("Alpha Options", Float) = 0</td>
+ </tr>
+ <tr>
+ <td>m_end_&lt;string></td>
+ <td>ends a dropdown menu that has been started with m_start_&lt;string></td>
+ <td>No</td>
+ <td>[HideInInspector] m_end_Alpha ("", Float) = 0</td>
+ </tr>
+ <tr>
+ <td>g_start_&lt;string></td>
+ <td>starts a group. properties are grouped together. not visible to the user. can be used to hide multiple properties with one condition specification.</td>
+ <td>No</td>
+ <td>[HideInInspector] g_start_blending ("--{condition_show:{type:PROPERTY_BOOL,data:_DisplayBlending}}", Float) = 0</td>
+ </tr>
+ <tr>
+ <td>g_end_&lt;string></td>
+ <td>ends a group that has been started with g_start_&lt;string></td>
+ <td>No</td>
+ <td>[HideInInspector] g_start_blending ("", Float) = 0</td>
+ </tr>
+ </table>
+ <h3>Suggestions:</h3>
+ <table>
+ <tr>
+ <th>Name</th>
+ <th>Description</th>
+ <th>Example</th>
+ </tr>
+ <tr>
+ <td>Variant Selector</td>
+ <td>Use a commbination of Enum and on_value_actions to create a varian selector</td>
+ <td>
+ [Enum(Cutout,0,Transparent,1)]variant_selector("Variant--{on_value_actions:[{value:0,actions:[{type:SET_PROPERTY,data:_ZWrite=1},{type:SET_SHADER,data:Thry/Example 1}]},{value:1,actions:[{type:SET_PROPERTY,data:_ZWrite=0},{type:SET_SHADER,data:Thry/Example 2}]}]}",Float) = 0
+ </td>
+ </tr>
+ </table>
+ <h2>Drawers</h2>
+ <table>
+ <tr>
+ <th>Drawer Name</th>
+ <th>Effect</th>
+ <th>Extra Property Options</th>
+ <th>Example</th>
+ </tr>
+ <tr>
+ <td>[SmallTexture]</td>
+ <td>Creates a texture field that will always be small</td>
+ <td></td>
+ <td>[SmallTexture]_MainTex("Main Texture",2D)= "white" { }</td>
+ </tr>
+ <tr>
+ <td>[BigTexture]</td>
+ <td>Creates a texture field that will always be big</td>
+ <td></td>
+ <td>[BigTexture]_MainTex("Main Texture",2D)= "white" { }</td>
+ </tr>
+ <tr>
+ <td>[StylizedBigTexture]</td>
+ <td>Creates a texture field that will always be big but stylized differently</td>
+ <td></td>
+ <td>[StylizedBigTexture]_MainTex("Main Texture",2D)= "white" { }</td>
+ </tr>
+ <tr>
+ <td>[Gradient]</td>
+ <td>
+ Creates a texture field with a gradient field next to it.
+ <br /> Gradient is automatically converted to texture.
+ </td>
+ <td></td>
+ <td>[Gradient]_ColorRamp("Color Ramp",2D)= "white" { }</td>
+ </tr>
+ <tr>
+ <td>[MultiSlider]</td>
+ <td>
+ Creates a slider for a range.
+ <br />Is used with a vector property.
+ <br />x and y are the slider values. z is slider minimum. w is slider maximum.
+ </td>
+ <td></td>
+ <td>[MultiSlider]_Slider("Multi Slider",Vector)= (0.1,0.9,0,1)</td>
+ </tr>
+ <tr>
+ <td>[TextureArray]</td>
+ <td>Creates field that accepts Texture Arrays</td>
+ <td></td>
+ <td>[TextureArray]_FlipbookTexArray ("Texture Array", 2DArray) = "" {}</td>
+ </tr>
+ <tr>
+ <td>[Vector2]</td>
+ <td>Creates a Vector 2 field</td>
+ <td></td>
+ <td>[Vector2]_Vector("Vector with 2 values",Vector)= (0,0,0,0)</td>
+ </tr>
+ <tr>
+ <td>[Vector3]</td>
+ <td>Creates Vector 3 field</td>
+ <td></td>
+ <td>[Vector3]_Vector("Vector with 3 values",Vector)= (0,0,0,0)</td>
+ </tr>
+ <tr>
+ <td>[Curve]</td>
+ <td>Creates a curve field</td>
+ <td></td>
+ <td>[Curve]_ColorCurve("Curve",2D)= "white" { }</td>
+ </tr>
+ <tr>
+ <td>[Helpbox]</td>
+ <td>Creates an info box</td>
+ <td></td>
+ <td>[Helpbox]_MainHelpbox("This is the text inside the info box",Float)= 0</td>
+ </tr>
+ </table>
+
+ <h2>Supported Default Unity Flags</h2>
+ <table>
+ <tr>
+ <th>Name</th>
+ <th>Effect</th>
+ </tr>
+ <tr>
+ <td>[NoScaleOffset]</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>[Normal]</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>[Space]</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>[Space(Int)]</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>[Toggle()]</td>
+ <td></td>
+ </tr>
+ </table>
+
+ <h2>Property Options</h2>
+ <b>
+ None of the poperty options are required.<br />
+ Options are defined in the display name of a property inside curly brackets and after "--":<br />
+ In practice:
+ </b><br /> _Tex("Texture--{Put all your options in here}",2D) = "white" { }<br />
+ _Tex("Texture--{offet:2,hover:read this on hover,altClick{type:URL,data:http://thryrallo.de}}",2D) = "white" { }<br />
+ <font color="red">
+ Use json syntax instead to future proof your properties!<br />
+ You can use '' instead of " inside property display names
+ </font><br />
+ Example:<br />
+ {''text'':''Youtube'',''action'':{''type'':''URL'',''data'':''https://www.youtube.com/''}
+ <table>
+ <tr>
+ <th>Name</th>
+ <th>Value Type</th>
+ <th>Effect</th>
+ <th>Drawer Specific</th>
+ <th>Example</th>
+ </tr>
+ <tr>
+ <td>offset</td>
+ <td>int</td>
+ <td>adds an extra x-offset to the property</td>
+ <td>No</td>
+ <td>_Tex("Texture--{offset:1}",2D) = "white" { }</td>
+ </tr>
+ <tr>
+ <td>tooltip</td>
+ <td>string</td>
+ <td>text that is shown when hovering above property</td>
+ <td>No</td>
+ <td>_Tex("Texture--{tooltip:use this texture for albedo}",2D) = "white" { }</td>
+ </tr>
+ <tr>
+ <td>altClick</td>
+ <td>Action</td>
+ <td>performs an action when holding alt down and clicking on property</td>
+ <td>No</td>
+ <td>_Tex("Texture--{altClick:{type:URL,data:http://thryrallo.de}}",2D) = "white" { }</td>
+ </tr>
+ <tr>
+ <td>condition_show</td>
+ <td>Condition</td>
+ <td>let's you define a condition that has to be true for this property to be shown in the ui</td>
+ <td>No</td>
+ <td>_Tex("Texture--{condition_show:{type:PROPERTY_BOOL,data:_ForceOpaque==1}}",2D) = "white" { }</td>
+ </tr>
+ <tr>
+ <td>condition_enable</td>
+ <td>Condition</td>
+ <td>let's you define a condition that has to be true for this property to be enabled</td>
+ <td>No</td>
+ <td>_Tex("Texture--{condition_enable:{type:PROPERTY_BOOL,data:_ForceOpaque==1}}",2D) = "white" { }</td>
+ </tr>
+ <tr>
+ <td>on_value_actions</td>
+ <td>PropertyValueAction[]</td>
+ <td>let's you define a actions that happen if this property is set to a specfiic value.</td>
+ <td>No</td>
+ <td>
+ [Enum(Cutout,0,Transparent,1)]variant_selector("Variant--{on_value_actions:[{value:0,actions:[{type:SET_PROPERTY,data:_ZWrite=1},{type:SET_SHADER,data:Thry/Example 1}]},{value:1,actions:[{type:SET_PROPERTY,data:_ZWrite=0},{type:SET_SHADER,data:Thry/Example 2}]}]}",Float) = 0
+ </td>
+ </tr>
+ <tr>
+ <td>button_right</td>
+ <td>Button</td>
+ <td>let's you define a button that is shown on the side of a dropdown header</td>
+ <td>Yes, only headers</td>
+ <td>_Tex("Texture--{button_right:{text:Test Button,action:{type:URL,data:https://github.com/Thryrallo/thryeditor},hover:hover text,condition_show:{type:PROPERTY_BOOL,data:_ShowButtonOnMenus}}}",2D) = "white" { }</td>
+ </tr>
+ <tr>
+ <td>texture</td>
+ <td>TextureData</td>
+ <td>Defines the texture settings for created textures.</td>
+ <td>[Gradient],[Curve]</td>
+ <td>[Gradient]_ColorRamp ("Gradient --{texture:{width:256,height:16,filterMode:Point,wrapMode:Clamp}}", 2D) = "white" { }<br />[Curve]_MainTex ("Texture --{image:{width:256,height:16,channel:b}}", 2D) = "white" { }</td>
+ </tr>
+ <tr>
+ <td>force_texture_options</td>
+ <td>bool</td>
+ <td>Default: false. Set this to true to hide the texture options and force your defined texture settings.</td>
+ <td>[Gradient]</td>
+ <td>[Gradient]_ColorRamp ("Gradient --{texture:{width:256,height:16,filterMode:Point,wrapMode:Clamp},force_texture_options:true}", 2D) = "white" { }</td>
+ </tr>
+ <tr>
+ <td>hide_in_inspector</td>
+ <td>bool</td>
+ <td>Default: false. Set this to true to hide the property in ThryEditor, but not the unity default inspector. Usefull if you already display the options in a texture dropdown or Foldout Header.</td>
+ <td>No</td>
+ <td>_Toogle ("Gradient Lighting--{texture:{hide_in_inspector:true}}", Int) = 0</td>
+ </tr>
+ <tr>
+ <td>reference_properties</td>
+ <td>string[]</td>
+ <td>Default: null. specified properties will be drawn in texture foldout menu</td>
+ <td>Texture</td>
+ <td>_Texture("Panning Texture --{reference_properties:[_PanSpeed,_PanUV]}", 2D) = "white" { }</td>
+ </tr>
+ <tr>
+ <td>reference_property</td>
+ <td>string</td>
+ <td>Specifies a property by it's name. </br>
+ If defined on a menu header, it will create a toggle linked with the referenced property. </br>
+ If defined on a TextureArray it will fill this float property with the texture array depth (frame count) after creating an array from a gif or multiple images.</br>
+ If defined on a texture property it will draw this property next to the texture property (for example for a color field)</td>
+ <td>[TextureArray],Texture,MenuHeader</td>
+ <td>Light and Shadow--{reference_property:_EnableLighting}<br />[TextureArray]_Texture("Animated Texture --{reference_property:_FrameCount}", 2DArray) = { }</td>
+ </tr>
+ <tr>
+ <td>is_hideable</td>
+ <td>bool</td>
+ <td>If set to true, property will be able to be hidden using the little eye icon in the top right of the inspector.</td>
+ <td>MenuHeaders</td>
+ <td>[HideInInspector] m_vertex("Vertex Options--{button_right:{text:Tutorial,action:{type:URL,data:https://www.youtube.com/watch?v=FO-bxI5znI0},hover:YouTube},is_hideable:true}", Float) = 0</td>
+ </tr>
+ <tr>
+ <td>is_hidden_default</td>
+ <td>bool</td>
+ <td>If set to true and property is hideable, the property will be hidden by default.</td>
+ <td>MenuHeaders</td>
+ <td>[HideInInspector] m_vertex("Vertex Options--{button_right:{text:Tutorial,action:{type:URL,data:https://www.youtube.com/watch?v=FO-bxI5znI0},hover:YouTube},is_hideable:true,is_hidden_default:true}", Float) = 0</td>
+ </tr>
+ </table>
+
+ <h2>Data Structures</h2>
+ <div>
+ <h3>Button</h3>
+ Variables:
+ <table>
+ <tr>
+ <th>Name</th>
+ <th>Value Type</th>
+ <th>required</th>
+ <th>default</th>
+ </tr>
+ <tr>
+ <td>text</td>
+ <td>string</td>
+ <td>text or texture</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>texture</td>
+ <td>TextureData</td>
+ <td>text or texture</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>hover</td>
+ <td>string</td>
+ <td>no</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>action</td>
+ <td>Action</td>
+ <td>kinda</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>condition_show</td>
+ <td>Condition</td>
+ <td>no</td>
+ <td></td>
+ </tr>
+ </table>
+ </div>
+ <div>
+ <h3>PropertyValueAction</h3>
+ Variables:
+ <table>
+ <tr>
+ <th>Name</th>
+ <th>Value Type</th>
+ <th>required</th>
+ </tr>
+ <tr>
+ <td>value</td>
+ <td>string</td>
+ <td>Yes</td>
+ </tr>
+ <tr>
+ <td>actions</td>
+ <td>Action[]</td>
+ <td>Yes</td>
+ </tr>
+ </table>
+ <h3>Action</h3>
+ Variables:
+ <table>
+ <tr>
+ <th>Name</th>
+ <th>Value Type</th>
+ <th>required</th>
+ </tr>
+ <tr>
+ <td>type</td>
+ <td>ActionType</td>
+ <td>Yes</td>
+ </tr>
+ <tr>
+ <td>data</td>
+ <td>string</td>
+ <td>Yes</td>
+ </tr>
+ </table>
+ <h3>Enum: ActionType</h3>
+ States:
+ <table>
+ <tr>
+ <th>Value</th>
+ <th>Effect</th>
+ <td>Example</td>
+ </tr>
+ <tr>
+ <td>URL</td>
+ <td>Opens the url in browser</td>
+ <td>{type:URL,data:https://github.com/Thryrallo/thryeditor}</td>
+ </tr>
+ <tr>
+ <td>SET_PROPERTY</td>
+ <td>Sets the value of a specified property.</td>
+ <td>{type:SET_PROPERTY,data:_ZWrite=1}</td>
+ </tr>
+ <tr>
+ <td>SET_SHADER</td>
+ <td>Changes the shader of the material.</td>
+ <td>{type:SET_SHADER,data:Thry/Example 1}</td>
+ </tr>
+ </table>
+ </div>
+ <div>
+ <h3>TextureData</h3>
+ Variables:
+ <table>
+ <tr>
+ <th>Name</th>
+ <th>Value Type</th>
+ <th>required</th>
+ <th>default</th>
+ <th>Explanation</th>
+ </tr>
+ <tr>
+ <td>name</td>
+ <td>string</td>
+ <td>no</td>
+ <td></td>
+ <td>File name of image</td>
+ </tr>
+ <tr>
+ <td>width</td>
+ <td>int</td>
+ <td>no</td>
+ <td>128</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>height</td>
+ <td>int</td>
+ <td>no</td>
+ <td>128</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>channel</td>
+ <td>char</td>
+ <td>no</td>
+ <td>r</td>
+ </tr>
+ <tr>
+ <td>ansioLevel</td>
+ <td>int</td>
+ <td>no</td>
+ <td>1</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>filterMode</td>
+ <td>Enum(FilterMode)</td>
+ <td>no</td>
+ <td>Bilinear</td>
+ <td>EnumValues: Bilinear,Point,Trilinear</td>
+ </tr>
+ <tr>
+ <td>wrapMode</td>
+ <td>Enum(TextureWrapMode)</td>
+ <td>no</td>
+ <td>Repeat</td>
+ <td>EnumValues: Clamp,Mirror,MirrorOnce,Repeat</td>
+ </tr>
+ </table>
+ </div>
+ <div>
+ <h3>Condition</h3>
+ Variables:
+ <table>
+ <tr>
+ <th>Name</th>
+ <th>Value Type</th>
+ <th>required</th>
+ <th>default</th>
+ </tr>
+ <tr>
+ <td>type</td>
+ <td>ConditionType</td>
+ <td>Yes</td>
+ <td>None</td>
+ </tr>
+ <tr>
+ <td>data</td>
+ <td>string</td>
+ <td>If type!=AND && type!=OR yes</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>condition1</td>
+ <td>Condition</td>
+ <td>if type==AND || type==OR yes</td>
+ <td>null</td>
+ </tr>
+ <tr>
+ <td>condition2</td>
+ <td>Condition</td>
+ <td>if type==AND || type==OR yes</td>
+ <td>null</td>
+ </tr>
+ </table>
+ Data will have different meanings depending on type<br />
+ <br />
+ <b>Data</b><br />
+ Useable Comparators: ==,!=,>,<
+ <table>
+ <tr>
+ <th>type</th>
+ <th>data value</th>
+ <th>example</th>
+ </tr>
+ <tr>
+ <td>PROPERTY_BOOL</td>
+ <td>&lt;PropertyName>(&lt;Comparator>&lt;value>)?</td>
+ <td>{type:PROPERTY_BOOL,data:_ForceOpaque}<br />or {type:PROPERTY_BOOL,data:_ForceOpaque==0}</td>
+ </tr>
+ <tr>
+ <td>EDITOR_VERSION</td>
+ <td>&lt;Comparator>&lt;value></td>
+ <td>{type:EDITOR_VERSION,data:>0.17} #checks if installed editor version > 0.17</td>
+ </tr>
+ <tr>
+ <td>VRC_SDK_VERSION</td>
+ <td>&lt;Comparator>&lt;value></td>
+ <td>{type:VRC_SDK_VERSION,data:>0.17} #checks if installed vrc sdk version > 0.17</td>
+ </tr>
+ <tr>
+ <td>TEXTURE_SET</td>
+ <td>&lt;PropertyName></td>
+ <td>{type:TEXTURE_SET,data:_ToonRamp} #checks if texture _ToonRamp is set</td>
+ </tr>
+ <tr>
+ <td>DROPDOWN</td>
+ <td>&lt;PropertyName>&lt;Comparator>&lt;value></td>
+ <td>{type:DROPDOWN,data:_LightingType==2} #checks if property _LightingType has enum 2 selected</td>
+ </tr>
+ </table>
+ <h3>Enum: ConditionType</h3>
+ States:
+ <table>
+ <tr>
+ <th>Value</th>
+ <th>Effect</th>
+ </tr>
+ <tr>
+ <td>AND</td>
+ <td>Combines two conditions with &&</td>
+ </tr>
+ <tr>
+ <td>OR</td>
+ <td>Combines two conditions with ||</td>
+ </tr>
+ <tr>
+ <td>PROPERTY_BOOL</td>
+ <td>checks agains the float value of a property</td>
+ </tr>
+ <tr>
+ <td>EDITOR_VERSION</td>
+ <td>checks agains the version number of ThryEditor</td>
+ </tr>
+ <tr>
+ <td>VRC_SDK_VERSION</td>
+ <td>checks agains the version number of the VRC SDK</td>
+ </tr>
+ </table>
+ </div>
+ </body>
+</html>
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/docs.html.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/docs.html.meta
new file mode 100644
index 00000000..5428cb04
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/docs.html.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 11eeab9ab5997bf419f83bfb9ffa1ba7
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/thry_editor_locale.csv b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/thry_editor_locale.csv
new file mode 100644
index 00000000..f0948283
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/thry_editor_locale.csv
@@ -0,0 +1,63 @@
+,English,German,Japanese,Spanish,Pirate,UwU,Binary,Chinese,
+translator,,Thryrallo,Ponponsan,Spanish Hub,,,,INYO,
+translation,Translation,Übersetzung,翻訳,Traducción,,,,翻译,
+,,,,,,,,,
+locale,Language,Sprache,言語,Language,Language,Wanguage,01001100 01100001 01101110 01100111 01110101 01100001 01100111 01100101,语言,
+locale_tooltip,Select Language for ThryEditor,Wähle die Sprache für ThryEditor,ThryEditorの言語を選択してください,Select Language for ThryEditor,Select Language for ThryEditor,Sewect wanguage fow thwyeditow,01010011 01100101 01101100 01100101 01100011 01110100 00100000 01001100 01100001 01101110 01100111 01110101 01100001 01100111 01100101 00100000 01100110 01101111 01110010 00100000 01010100 01101000 01110010 01111001 01000101 01100100 01101001 01110100 01101111 01110010,选择Thry编辑器窗口的语言,
+,,,,,,,,,
+first_install_message,Hello. You just installed a shader using the Thry Shader Editor for it's UI. This is the editor's settings window. It offers you some customizability. You can close this window and open it back up over your menu if you want to change something.,Thry Shader Editor wurde installiert. Dies ist das Fenster für die Editor Einstellungen.,正常にThry Shader Editorはインストールされました。こちらがエディター設定画面になります。,Se ha actualizado exitosamente Thry Shader Editor. Esta es la ventana de configuración del editor.,Thry shader editor successfully installed. This here be the editor settings window.,Thwy shadew editow successfuwwy instawwed. Thiws iws the editow settings window.,01010100 01101000 01110010 01111001 00100000 01010011 01101000 01100001 01100100 01100101 01110010 00100000 01000101 01100100 01101001 01110100 01101111 01110010 00100000 01110011 01110101 01100011 01100011 01100101 01110011 01110011 01100110 01110101 01101100 01101100 01111001 00100000 01101001 01101110 01110011 01110100 01100001 01101100 01101100 01100101 01100100 00101110 00100000 01010100 01101000 01101001 01110011 00100000 01101001 01110011 00100000 01110100 01101000 01100101 00100000 01100101 01100100 01101001 01110100 01101111 01110010 00100000 01110011 01100101 01110100 01110100 01101001 01101110 01100111 01110011 00100000 01110111 01101001 01101110 01100100 01101111 01110111 00101110,你好。你剛剛完成安装了一个使用Thry Shader编辑器UI的着色器。這是編輯器的設置窗口。它為您提供了一些自定义能力。如果您要更改某些內容,可以關閉此窗口並在菜單上重新打開此窗口。,
+update_message,Thry editor has been updated,Thry editor wurde aktualisiert,Thry editorは新しいバージョンに更新されました,Thry editor ha sido actualizado.,Thry editor has been updated,Thwy editow has bewn updated,01010100 01101000 01110010 01111001 00100000 01100101 01100100 01101001 01110100 01101111 01110010 00100000 01101000 01100001 01110011 00100000 01100010 01100101 01100101 01101110 00100000 01110101 01110000 01100100 01100001 01110100 01100101 01100100,Thry编辑器已更新完成,
+downgrade_message,Warning: The Version of Thry Editor has declined,Warning: Die Version von Thry Editor hat sich verringert,注意!: Thry Editorが下位のバージョンに変更されました,Advertencia: la versión de Thry Editor ha esta desactualizada.,Warning: the version o' thry editor 'as declined,Wawning: the vewsion of thwy editow has decwined,01010111 01100001 01110010 01101110 01101001 01101110 01100111 00111010 00100000 01010100 01101000 01100101 00100000 01010110 01100101 01110010 01110011 01101001 01101111 01101110 00100000 01101111 01100110 00100000 01010100 01101000 01110010 01111001 00100000 01000101 01100100 01101001 01110100 01101111 01110010 00100000 01101000 01100001 01110011 00100000 01100100 01100101 01100011 01101100 01101001 01101110 01100101 01100100,警告:Thry编辑器的版本已下降,
+,,,,,,,,,
+header_editor,Editor,Editor,エディター,Editor,,Editor,01000101 01100100 01101001 01110100 01101111 01110010,编辑器,
+,,,,,,,,,
+header_modules,Install add-on Modules,Installiere add-on Module,追加モジュール,Modulos extra,Install add-on Modules,Install add-on Modules,01000101 01111000 01110100 01110010 01100001 00100000 01001101 01101111 01100100 01110101 01101100 01100101 01110011,安装额外功能模组,
+header_thrird_party,Third Party Tools,,,,,,,第三方工具,
+,,,,,,,,,
+newest,newest,neuste,最新,el más nuevo,,newest,01101110 01100101 01110111 01100101 01110011 01110100,最新,
+version,version,version,バージョン,version,,vewsion,01110110 01100101 01110010 01110011 01101001 01101111 01101110,版本,
+user,user,Benutzer,ユーザー,Usuario,,usew,01110101 01110011 01100101 01110010,用户,
+requirements,Requirements,Voraussetzungen,必要バージョン,Requisitos,,Wequiwements,01010010 01100101 01110001 01110101 01101001 01110010 01100101 01101101 01100101 01101110 01110100 01110011,要求,
+add,Add,Hinzufügen,追加,Añadir,,Add,01000001 01100100 01100100,添加,
+delete,Delete,Löschen,削除,Borrar,,Dewete,01000100 01100101 01101100 01100101 01110100 01100101,删除,
+save,Save,Speichern,保存,Guardar,,Save,01010011 01100001 01110110 01100101,保存,
+,,,,,,,,,
+default_texture_type,Default Texture Display,Texturen Darstellung,デフォルト表示テクスチャー,Visualización de textura predeterminada,Default Texture Display,Defauwt textuwe dispway,01000100 01100101 01100110 01100001 01110101 01101100 01110100 00100000 01010100 01100101 01111000 01110100 01110101 01110010 01100101 00100000 01000100 01101001 01110011 01110000 01101100 01100001 01111001,默认贴图显示,
+default_texture_type_tooltip,Select how your textures should be displayed if the property doesn't force the type,Wähle wie deine Texture Menüs dargestellt werden.,プロパティーに指定されなかった場合にどうテクスチャーが表示されるか指定してください,Seleccione cómo se deben mostrar sus texturas si la propiedad no fuerza el tipo,Select 'ow yer textures should be displayed if the property doesn't force the type,Sewect how youw textuwes shouwd be dispwayed if the pwopewty doesn't fowce the type,01010011 01100101 01101100 01100101 01100011 01110100 00100000 01101000 01101111 01110111 00100000 01111001 01101111 01110101 01110010 00100000 01110100 01100101 01111000 01110100 01110101 01110010 01100101 01110011 00100000 01110011 01101000 01101111 01110101 01101100 01100100 00100000 01100010 01100101 00100000 01100100 01101001 01110011 01110000 01101100 01100001 01111001 01100101 01100100 00100000 01101001 01100110 00100000 01110100 01101000 01100101 00100000 01110000 01110010 01101111 01110000 01100101 01110010 01110100 01111001 00100000 01100100 01101111 01100101 01110011 01101110 00100111 01110100 00100000 01100110 01101111 01110010 01100011 01100101 00100000 01110100 01101000 01100101 00100000 01110100 01111001 01110000 01100101,选择当属性未强制指定类型时的帖图显示类型,
+showRenderQueue,Show Render Queue,Zeige Render Queue,Render Queue(描画順)の表示,Mostrar Render Queue,Show Render Queue,Show wendew queue,01010011 01101000 01101111 01110111 00100000 01010010 01100101 01101110 01100100 01100101 01110010 00100000 01010001 01110101 01100101 01110101 01100101,显示渲染队列(Render Queue),
+showRenderQueue_tooltip,enable a render queue selector,"erlaubt dem render queue wähler auch mit vrchat zu funktionieren, indem er kopien des orginal shaders mit der neuen render queue erzeugt.",Render Queue(描画順)セレクターの有効化,Habilitar un selector de render queue.,enable a render queue selector,enabwe a wendew queue sewectow,01100101 01101110 01100001 01100010 01101100 01100101 00100000 01100001 00100000 01110010 01100101 01101110 01100100 01100101 01110010 00100000 01110001 01110101 01100101 01110101 01100101 00100000 01110011 01100101 01101100 01100101 01100011 01110100 01101111 01110010,启用渲染队列选择器,
+allowCustomLockingRenaming,Allow custom renaming for locking,,,,,,,,
+allowCustomLockingRenaming_tooltip,,,,,,,,,
+showManualReloadButton,Show Manual Reload Button,,,,,,,,
+showManualReloadButton_tooltip,Show button to force the ui to reload,,,,,,,,
+gradient_name,Gradient Save File Names,Gradient Datei Namen,グラデーションの保存名,Nombres de archivo de guardado de gradiente,Gradient Save File Names,Gwadient save fiwe names,01000111 01110010 01100001 01100100 01101001 01100101 01101110 01110100 00100000 01010011 01100001 01110110 01100101 00100000 01000110 01101001 01101100 01100101 00100000 01001110 01100001 01101101 01100101 01110011,渐层梯度贴图保存名称命名,
+gradient_name_tooltip,"configures the way gradient texture files are named. use <material>, <hash> and <prop> to identify the texture","Definiert wie gradient Datein benannt werden. benutze <material>, <hash> und <prop> um die texture zu indentifizieren",シェーダーインポート時にポップアップを表示する,"Configura la forma en que se nombran los archivos de textura de gradiente. use <material>, <hash> y <prop> para identificar la textura","configures the way gradient texture files are named. use <material>, <hash> and <prop> to identify the texture","configuwes the way gwadient textuwe fiwes awe named. use <material>, <hash> awnd <prop> tuwu identify the textuwe",01100011 01101111 01101110 01100110 01101001 01100111 01110101 01110010 01100101 01110011 00100000 01110100 01101000 01100101 00100000 01110111 01100001 01111001 00100000 01100111 01110010 01100001 01100100 01101001 01100101 01101110 01110100 00100000 01110100 01100101 01111000 01110100 01110101 01110010 01100101 00100000 01100110 01101001 01101100 01100101 01110011 00100000 01100001 01110010 01100101 00100000 01101110 01100001 01101101 01100101 01100100 00101110 00100000 01110101 01110011 01100101 00100000 00111100 01101101 01100001 01110100 01100101 01110010 01101001 01100001 01101100 00111110 00101100 00100000 00111100 01101000 01100001 01110011 01101000 00111110 00100000 01100001 01101110 01100100 00100000 00111100 01110000 01110010 01101111 01110000 00111110 00100000 01110100 01101111 00100000 01101001 01100100 01100101 01101110 01110100 01101001 01100110 01111001 00100000 01110100 01101000 01100101 00100000 01110100 01100101 01111000 01110100 01110101 01110010 01100101,设置渐层梯度贴图的命名规则。使用 <material>, <hash> 和 <prop> 来定义贴图
+gradient_add_hash_or_prop,Consider adding <hash> or <prop>.,Consider adding <hash> or <prop>.,名前に<hash>または<prop>を追加することを推奨します。,Considere agregar <hash> o <prop>.,Consider adding <hash> or <prop>.,Considew adding <hash> ow <prop>.,01000011 01101111 01101110 01110011 01101001 01100100 01100101 01110010 00100000 01100001 01100100 01100100 01101001 01101110 01100111 00100000 00111100 01101000 01100001 01110011 01101000 00111110 00100000 01101111 01110010 00100000 00111100 01110000 01110010 01101111 01110000 00111110 00101110,推荐添加 <hash> 或 <prop>,
+gradient_add_material,Consider adding <material>.,Consider adding <material>.,名前に<material>を追加することを推奨します。,Considere agregar <material>.,Consider adding <material>.,Considew adding <material>.,01000011 01101111 01101110 01110011 01101001 01100100 01100101 01110010 00100000 01100001 01100100 01100100 01101001 01101110 01100111 00100000 00111100 01101101 01100001 01110100 01100101 01110010 01101001 01100001 01101100 00111110 00101110,推荐添加 <material>,
+gradient_add_material_or_prop,Add <material> <hash> or <prop> to destingish between gradients.,Füge <material> <hash> oder <prop> hinzu um zwischen gradients unterscheiden zu können.,名前に<material>、<hash>または<prop>を追加して識別可能にしてください。,Agregue <material> <hash> o <prop> para distinguir entre gradientes.,Add <material> <hash> or <prop> to destingish between gradients.,Add <material> <hash> ow <prop> tuwu destingish between gwadients.,01000001 01100100 01100100 00100000 00111100 01101101 01100001 01110100 01100101 01110010 01101001 01100001 01101100 00111110 00100000 00111100 01101000 01100001 01110011 01101000 00111110 00100000 01101111 01110010 00100000 00111100 01110000 01110010 01101111 01110000 00111110 00100000 01110100 01101111 00100000 01100100 01100101 01110011 01110100 01101001 01101110 01100111 01101001 01110011 01101000 00100000 01100010 01100101 01110100 01110111 01100101 01100101 01101110 00100000 01100111 01110010 01100001 01100100 01101001 01100101 01101110 01110100 01110011 00101110,添加 <material> <hash> 或 <prop> 以识别不同的渐层梯度贴图,
+gradient_good_naming,Good naming.,Gute Benennung.,正常な名前です、いいセンスだ。,Buen nombre.,Good naming.,Good naming.,01000111 01101111 01101111 01100100 00100000 01101110 01100001 01101101 01101001 01101110 01100111 00101110,良好的名称,
+,,,,,,,,,
+autoMarkPropertiesAnimated,Automatically mark animated properties,,,,,,,,
+autoMarkPropertiesAnimated_tooltip,Automatically mark properties as animated when changed their value while in animation mode.,,,,,,,,
+,,,,,,,,,
+auto_lock_dialog,"{0} material(s) have not been locked and will now be locked automatically. Locking in can dramatically improve runtime performance.\n\nDuring this time unity will remain unresponsive, please be patient.",,,,,,,,
+lockin_button_single,Lock In Optimized Shader,Material optimieren und sperren,,,,,,,
+lockin_button_multi,Lock in Optimized Shaders ({0} materials),{0} Materialien optimieren und sperren,,,,,,,
+unlock_button_single,Unlock Shader,Material entsperren,,,,,,,
+unlock_button_multi,Unlock Shaders ({0} materials),{0} Materialien entsperren,,,,,,,
+,,,,,,,,,
+preset_material_notify,This material is a preset.,Dieses Material definiert eine Voreinstellung.,,,,,,,
+preset_name,Preset Name,Voreinstellung Benennung,,,,,,,
+preset_revert,Revert Preset: ,Voreinstellung rückgänig machen: ,,,,,,,
+,,,,,,,,,
+yes,Yes,,,,,,,,
+no,No,,,,,,,,
+autoSetAnchorOverride,Auto Set Anchor Override,,,,,,,,
+autoSetAnchorOverride_tooltip,,,,,,,,,
+humanBoneAnchor,Human Bone Anchor,,,,,,,,
+humanBoneAnchor_tooltip,Humanoid bone to use as the anchor override if GameObject not found.,,,,,,,,
+anchorOverrideObjectName,Object Anchor Name,,,,,,,,
+anchorOverrideObjectName_tooltip,Name of a GameObject somewhere in your avatar's hierarchy to use as an anchor.\nIf not found the humanoid bone will be used.,,,,,,,,
+autoAnchorDialog_Title,Bad Lighting Warning,,,,,,,,
+autoAnchorDialog_Text,Your avatar's renderers don't all have an anchor override set.\nThis can cause flickering and uneven lighting on your avatar in some worlds.\nWould you like to enable settings these automatically?\n\nYou can change this in Thry settings.,,,,,,,,
+autoAnchorError_NotHumanoid,Auto Anchor Setter: Avatar {0} is not humanoid or is missing an Animator.,,,,,,,,
diff --git a/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/thry_editor_locale.csv.meta b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/thry_editor_locale.csv.meta
new file mode 100644
index 00000000..2f8880dc
--- /dev/null
+++ b/VRCSDK3Avatars/Assets/_PoiyomiShaders/Scripts/ThryEditor/thry_editor_locale.csv.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 30877c6905d94034c8c91a9aba08fc01
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant: