using System.Collections; using System.Collections.Generic; using System.Reflection; using System; using UnityEditor; using UnityEngine; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering; using UnityEngine.UIElements; using Unity.Collections; using Unity.Mathematics; using System.IO; using Object = UnityEngine.Object; namespace SLZ.SLZEditorTools { public static class ShaderGUIUtils { const string shaderGUIStylePath = "Packages/com.unity.render-pipelines.universal/Editor/ShaderGUI/Styles/ShaderGUIStyles.uss"; static StyleSheet s_ShaderGUISheet; public static StyleSheet shaderGUISheet { get { if (s_ShaderGUISheet == null) { s_ShaderGUISheet = AssetDatabase.LoadAssetAtPath(shaderGUIStylePath); if (s_ShaderGUISheet == null) { Debug.LogError("Failed to find Shader GUI Style Sheet at " + shaderGUIStylePath); } } return s_ShaderGUISheet; } } static FieldInfo s_InaccessibleToggle; /// /// Makes a foldout look like a unity inspector header. /// /// /// /// public static void SetHeaderStyle(Foldout f, string title, Texture iconTex = null, Toggle headerToggle = null) { f.AddToClassList("headerRoot"); if (s_InaccessibleToggle == null) { s_InaccessibleToggle = typeof(Foldout).GetField("m_Toggle", BindingFlags.NonPublic | BindingFlags.Instance); } Toggle actualFuckingToggle = (Toggle)s_InaccessibleToggle.GetValue(f); actualFuckingToggle.AddToClassList("headerTogglebar"); actualFuckingToggle.style.paddingBottom = 3; actualFuckingToggle.style.paddingTop = 2; actualFuckingToggle.style.marginBottom = 1; VisualElement container = actualFuckingToggle.ElementAt(0); container.style.marginRight = 0; container.style.alignItems = Align.Center; VisualElement dropdown = container.ElementAt(0); dropdown.style.marginLeft = 5; Label titleLabel = new Label(title); titleLabel.style.unityFontStyleAndWeight = FontStyle.Bold; titleLabel.style.marginLeft = 6; titleLabel.style.unityTextAlign = TextAnchor.UpperLeft; titleLabel.style.paddingTop = 0; if (iconTex != null) { Image icon = new Image(); icon.image = iconTex; icon.style.height = 16; icon.style.width = 16; icon.style.minWidth = 16; icon.style.minHeight = 16; icon.style.maxWidth = 16; icon.style.maxHeight = 16; icon.style.marginRight = 0; icon.style.marginLeft = 0; icon.style.marginTop = 0; icon.style.marginBottom = 0; // icon.style.ba //icon.style.scale = new Vector2(1.1f, 1.1f); container.Add(icon); } if (headerToggle == null) { headerToggle = new Toggle(); headerToggle.style.visibility = Visibility.Hidden; } headerToggle.style.marginLeft = 6; headerToggle.style.marginRight = 0; headerToggle.style.marginBottom = 1; container.Add(headerToggle); container.Add(titleLabel); f.contentContainer.AddToClassList("headerContent"); } // Gets the shader property index corresponding to each element of a material property array public static int[] GetMaterialPropertyShaderIdx(MaterialProperty[] materialProperties, Shader shader) { int numMatProps = materialProperties.Length; int[] propertyIdx = new int[numMatProps]; for (int i = 0; i < numMatProps; i++) { propertyIdx[i] = shader.FindPropertyIndex(materialProperties[i].name); } return propertyIdx; } public static int[] GetShaderIdxToMaterialProp(MaterialProperty[] materialProperties, Shader shader) { int numMatProps = materialProperties.Length; int[] propertyIdx = new int[shader.GetPropertyCount()]; for (int i = 0; i < numMatProps; i++) { //propertyIdx[shader.FindPropertyIndex(materialProperties[i].name)] = i; propertyIdx[i] = i; } return propertyIdx; } /// /// Strip out unused texture references in materials to avoid unity bundling/loading them /// /// target objects, assumed to all be materials /// Array of material properties retrieved from MaterialEditor.GetMaterialProperties /// map from each element of materialProperties to the index of its shader property /// Shader used by all the target materials public static void SanitizeMaterials(Object[] targets, MaterialProperty[] materialProperties, int[] propertyIdx, Shader shader) { int numMatProps = materialProperties.Length; HashSet validTextureNames = new HashSet(); for (int i = 0; i < numMatProps; i++) { ShaderPropertyType type = shader.GetPropertyType(propertyIdx[i]); switch (type) { case ShaderPropertyType.Texture: validTextureNames.Add(materialProperties[i].name); break; } } int numMats = targets.Length; for (int mat = 0; mat < numMats; mat++) { SerializedObject smat = new SerializedObject(targets[mat]); SerializedProperty texEnv = smat.FindProperty("m_SavedProperties.m_TexEnvs"); bool removedProp = false; string removedPropNames = "\n "; if (texEnv != null) { int numTex = texEnv.arraySize; for (int tIdx = numTex - 1; tIdx >= 0; tIdx--) { SerializedProperty nameProp = smat.FindProperty("m_SavedProperties.m_TexEnvs.Array.data[" + tIdx.ToString() + "].first"); string name = nameProp.stringValue; if (!validTextureNames.Contains(name)) { texEnv.DeleteArrayElementAtIndex(tIdx); removedPropNames += name + "\n "; removedProp = true; } } } if (removedProp) { Debug.LogWarning("LitMAS GUI: Removed the following unused texture properties from " + targets[mat].name + removedPropNames); smat.ApplyModifiedProperties(); } smat.Dispose(); } } static Dictionary icon16px = new Dictionary(); public static Texture2D GetClosestUnityIconMip(string textureName, int iconHeightInPts) { int closestPow2Res = (int)math.round(math.log2(iconHeightInPts * EditorGUIUtility.pixelsPerPoint)); int iconRes = 1 << closestPow2Res; string key = textureName + "X" + iconRes.ToString(); if (icon16px.ContainsKey(key)) { Texture2D storedIcon = icon16px[key]; if (storedIcon != null) { return icon16px[key]; } else { icon16px.Remove(key); } } GUIContent imguiIcon = EditorGUIUtility.IconContent(textureName); if (imguiIcon == null) { return null; } Texture2D icon = imguiIcon.image as Texture2D; if (icon == null) { return null; } if (icon.width < (iconRes + (iconRes / 2))) { return icon; } int numMips = icon.mipmapCount; int desiredMip = numMips - (closestPow2Res + 1); if (desiredMip < 1) { return icon; } Texture2D tex = new Texture2D( Mathf.Max(icon.width >> desiredMip, 1), Mathf.Max(icon.height >> desiredMip, 1), icon.graphicsFormat, TextureCreationFlags.MipChain); tex.hideFlags = HideFlags.DontSaveInEditor | HideFlags.DontSaveInBuild; tex.name = textureName; int currentMip = 0; NativeArray iconData = new NativeArray((int)GraphicsFormatUtility.ComputeMipmapSize(tex.width, tex.height, tex.graphicsFormat), Allocator.Persistent); while (desiredMip < numMips && currentMip < tex.mipmapCount) { NativeArray texData = tex.GetPixelData(currentMip); AsyncGPUReadbackRequest request = AsyncGPUReadback.RequestIntoNativeArray(ref iconData, icon, desiredMip); request.WaitForCompletion(); NativeArray.Copy(iconData, texData, texData.Length); currentMip++; desiredMip++; texData.Dispose(); } iconData.Dispose(); tex.Apply(false, true); icon16px.Add(key, tex); return tex; } static Action s_IncrementVersion; public static void UpdateVisualElement(VisualElement v) { if (s_IncrementVersion == null) { MethodInfo mi = typeof(VisualElement).GetMethod("IncrementVersion", BindingFlags.NonPublic | BindingFlags.Instance); s_IncrementVersion = (Action) mi.CreateDelegate(typeof(Action)); } s_IncrementVersion.Invoke(v, 8 | 2048); } static MethodInfo getTracker; static PropertyInfo s_PropertyViewer; static void ReflectEditorTracker() { s_PropertyViewer = typeof(Editor).GetProperty("propertyViewer", BindingFlags.NonPublic | BindingFlags.Instance); if (s_PropertyViewer == null) Debug.Log("NULL Property Viewer field"); Type viewerInterface = s_PropertyViewer.PropertyType; if (viewerInterface == null) Debug.Log("NULL Property Viewer type"); getTracker = viewerInterface.GetProperty("tracker", BindingFlags.Public | BindingFlags.Instance).GetGetMethod(); } public static void ForceRebuild(Editor e) { if (s_PropertyViewer == null) { ReflectEditorTracker(); } object propertyViewer = s_PropertyViewer.GetValue(e); if (propertyViewer != null) { var map = propertyViewer.GetType().GetInterfaceMap(getTracker.DeclaringType); int index = Array.IndexOf(map.InterfaceMethods, getTracker); if (index < 0) return; object tracker = map.InterfaceMethods[index].Invoke(propertyViewer, null); ActiveEditorTracker activeEditorTracker = (ActiveEditorTracker) tracker; //activeEditorTracker.ForceRebuild(); } } } }