289 lines
12 KiB
C#
289 lines
12 KiB
C#
|
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<StyleSheet>(shaderGUIStylePath);
|
||
|
if (s_ShaderGUISheet == null)
|
||
|
{
|
||
|
Debug.LogError("Failed to find Shader GUI Style Sheet at " + shaderGUIStylePath);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return s_ShaderGUISheet;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static FieldInfo s_InaccessibleToggle;
|
||
|
/// <summary>
|
||
|
/// Makes a foldout look like a unity inspector header.
|
||
|
/// </summary>
|
||
|
/// <param name="f"></param>
|
||
|
/// <param name="title"></param>
|
||
|
/// <param name="iconTex"></param>
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
|
||
|
/// <summary>
|
||
|
/// Strip out unused texture references in materials to avoid unity bundling/loading them
|
||
|
/// </summary>
|
||
|
/// <param name="targets">target objects, assumed to all be materials</param>
|
||
|
/// <param name="materialProperties">Array of material properties retrieved from MaterialEditor.GetMaterialProperties</param>
|
||
|
/// <param name="propertyIdx">map from each element of materialProperties to the index of its shader property</param>
|
||
|
/// <param name="shader">Shader used by all the target materials</param>
|
||
|
public static void SanitizeMaterials(Object[] targets, MaterialProperty[] materialProperties, int[] propertyIdx, Shader shader)
|
||
|
{
|
||
|
int numMatProps = materialProperties.Length;
|
||
|
|
||
|
HashSet<string> validTextureNames = new HashSet<string>();
|
||
|
|
||
|
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<string, Texture2D> icon16px = new Dictionary<string, Texture2D>();
|
||
|
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<byte> iconData = new NativeArray<byte>((int)GraphicsFormatUtility.ComputeMipmapSize(tex.width, tex.height, tex.graphicsFormat), Allocator.Persistent);
|
||
|
while (desiredMip < numMips && currentMip < tex.mipmapCount)
|
||
|
{
|
||
|
NativeArray<byte> texData = tex.GetPixelData<byte>(currentMip);
|
||
|
AsyncGPUReadbackRequest request = AsyncGPUReadback.RequestIntoNativeArray<byte>(ref iconData, icon, desiredMip);
|
||
|
request.WaitForCompletion();
|
||
|
NativeArray<byte>.Copy(iconData, texData, texData.Length);
|
||
|
currentMip++;
|
||
|
desiredMip++;
|
||
|
texData.Dispose();
|
||
|
}
|
||
|
iconData.Dispose();
|
||
|
tex.Apply(false, true);
|
||
|
icon16px.Add(key, tex);
|
||
|
return tex;
|
||
|
}
|
||
|
|
||
|
|
||
|
static Action<VisualElement, int> 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<VisualElement, int>) mi.CreateDelegate(typeof(Action<VisualElement, int>));
|
||
|
}
|
||
|
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();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|