Fix : 닐로툰 버전 업데이트
This commit is contained in:
parent
6f16a69a4d
commit
ce7f7c51c9
BIN
Assets/NiloToonURP/CHANGELOG.md
(Stored with Git LFS)
BIN
Assets/NiloToonURP/CHANGELOG.md
(Stored with Git LFS)
Binary file not shown.
@ -101,6 +101,7 @@ namespace NiloToon.NiloToonURP
|
||||
|
||||
// Mapping from original materials to cloned materials
|
||||
Dictionary<Material, Material> materialMap = new Dictionary<Material, Material>();
|
||||
List<string> createdMaterialAssetPaths = new List<string>();
|
||||
|
||||
foreach (Material mat in materialsSet)
|
||||
{
|
||||
@ -118,6 +119,7 @@ namespace NiloToon.NiloToonURP
|
||||
AssetDatabase.CreateAsset(matClone, newMatPath);
|
||||
Debug.Log("Created Material Asset: " + newMatPath);
|
||||
materialMap[mat] = matClone;
|
||||
createdMaterialAssetPaths.Add(newMatPath);
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
@ -131,6 +133,18 @@ namespace NiloToon.NiloToonURP
|
||||
|
||||
// Instantiate the selected prefab
|
||||
GameObject instance = (GameObject)PrefabUtility.InstantiatePrefab(prefab);
|
||||
if (instance == null)
|
||||
{
|
||||
Debug.LogError($"Failed to instantiate prefab before generating NiloToon variant: {assetPath}");
|
||||
CleanupGeneratedMaterialAssets(createdMaterialAssetPaths);
|
||||
return;
|
||||
}
|
||||
|
||||
int removedMissingScriptCount = RemoveMissingMonoBehavioursRecursive(instance);
|
||||
if (removedMissingScriptCount > 0)
|
||||
{
|
||||
Debug.LogWarning($"Removed {removedMissingScriptCount} missing MonoBehaviour references from temporary prefab instance before generating NiloToon variant.");
|
||||
}
|
||||
|
||||
Debug.Log($"Attempting to save prefab variant at path: {variantPath}");
|
||||
|
||||
@ -144,6 +158,7 @@ namespace NiloToon.NiloToonURP
|
||||
Debug.LogError($"Failed to save prefab variant at path: {variantPath}");
|
||||
Debug.LogException(e);
|
||||
GameObject.DestroyImmediate(instance);
|
||||
CleanupGeneratedMaterialAssets(createdMaterialAssetPaths);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -177,7 +192,10 @@ namespace NiloToon.NiloToonURP
|
||||
}
|
||||
|
||||
// Run auto setup for the prefab variant
|
||||
NiloToonEditorPerCharacterRenderControllerCustomEditor.AutoSetupCharacterGameObject(prefabVariantContents);
|
||||
NiloToonEditorPerCharacterRenderControllerCustomEditor.AutoSetupCharacterGameObject(
|
||||
prefabVariantContents,
|
||||
shouldPromptMaterialEditConfirmation: false,
|
||||
shouldEditMaterialWhenConfirmationSkipped: true);
|
||||
|
||||
// Save and unload prefab variant
|
||||
PrefabUtility.SaveAsPrefabAsset(prefabVariantContents, variantPath);
|
||||
@ -187,6 +205,17 @@ namespace NiloToon.NiloToonURP
|
||||
EditorUtility.DisplayDialog("Success", "Prefab variant created with cloned materials.", "OK");
|
||||
}
|
||||
|
||||
static void CleanupGeneratedMaterialAssets(List<string> createdMaterialAssetPaths)
|
||||
{
|
||||
foreach (string materialAssetPath in createdMaterialAssetPaths)
|
||||
{
|
||||
AssetDatabase.DeleteAsset(materialAssetPath);
|
||||
}
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
[MenuItem("Window/NiloToonURP/[Prefab] Create NiloToon Prefab Variant and Materials", priority = 0, validate = true)]
|
||||
[MenuItem("Assets/NiloToon/[Prefab] Create NiloToon Prefab Variant and Materials", priority = 1100 + 0, validate = true)]
|
||||
public static bool ValidateCreatePrefabVariantAndCloneMaterials()
|
||||
@ -270,5 +299,17 @@ namespace NiloToon.NiloToonURP
|
||||
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
static int RemoveMissingMonoBehavioursRecursive(GameObject root)
|
||||
{
|
||||
int removedCount = 0;
|
||||
|
||||
foreach (Transform transform in root.GetComponentsInChildren<Transform>(true))
|
||||
{
|
||||
removedCount += GameObjectUtility.RemoveMonoBehavioursWithMissingScript(transform.gameObject);
|
||||
}
|
||||
|
||||
return removedCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -168,6 +168,14 @@ namespace NiloToon.NiloToonURP
|
||||
}
|
||||
|
||||
public static void AutoSetupCharacterGameObject(GameObject gameobject)
|
||||
{
|
||||
AutoSetupCharacterGameObject(gameobject, shouldPromptMaterialEditConfirmation: true);
|
||||
}
|
||||
|
||||
public static void AutoSetupCharacterGameObject(
|
||||
GameObject gameobject,
|
||||
bool shouldPromptMaterialEditConfirmation,
|
||||
bool shouldEditMaterialWhenConfirmationSkipped = true)
|
||||
{
|
||||
var charScript = gameobject.GetComponent<NiloToonPerCharacterRenderController>();
|
||||
if (!charScript)
|
||||
@ -175,9 +183,20 @@ namespace NiloToon.NiloToonURP
|
||||
charScript = gameobject.AddComponent<NiloToonPerCharacterRenderController>();
|
||||
}
|
||||
|
||||
AutoSetupCharacterGameObject(charScript);
|
||||
AutoSetupCharacterGameObject(
|
||||
charScript,
|
||||
shouldPromptMaterialEditConfirmation,
|
||||
shouldEditMaterialWhenConfirmationSkipped);
|
||||
}
|
||||
public static void AutoSetupCharacterGameObject(NiloToonPerCharacterRenderController perCharScript)
|
||||
{
|
||||
AutoSetupCharacterGameObject(perCharScript, shouldPromptMaterialEditConfirmation: true);
|
||||
}
|
||||
|
||||
public static void AutoSetupCharacterGameObject(
|
||||
NiloToonPerCharacterRenderController perCharScript,
|
||||
bool shouldPromptMaterialEditConfirmation,
|
||||
bool shouldEditMaterialWhenConfirmationSkipped = true)
|
||||
{
|
||||
if (!perCharScript)
|
||||
{
|
||||
@ -188,14 +207,17 @@ namespace NiloToon.NiloToonURP
|
||||
}
|
||||
perCharScript.RefillAllRenderers();
|
||||
|
||||
bool shouldEditMaterial = EditorUtility.DisplayDialog(
|
||||
$"Auto setup character - {perCharScript.gameObject.name}",
|
||||
"Set up NiloToon's script on this character completed.\n\n" +
|
||||
"Do you want to convert the materials to NiloToon also?\n" +
|
||||
"(convert from lilToon/MToon/UniUnlit/URP Lit/URP Unlit to NiloToon_Character)",
|
||||
"Yes, convert the materials to NiloToon.",
|
||||
"No, DO NOT edit any materials."
|
||||
);
|
||||
bool shouldEditMaterial = ResolveShouldEditMaterialDecision(
|
||||
shouldPromptMaterialEditConfirmation,
|
||||
shouldEditMaterialWhenConfirmationSkipped,
|
||||
() => EditorUtility.DisplayDialog(
|
||||
$"Auto setup character - {perCharScript.gameObject.name}",
|
||||
"Set up NiloToon's script on this character completed.\n\n" +
|
||||
"Do you want to convert the materials to NiloToon also?\n" +
|
||||
"(convert from lilToon/MToon/UniUnlit/URP Lit/URP Unlit to NiloToon_Character)",
|
||||
"Yes, convert the materials to NiloToon.",
|
||||
"No, DO NOT edit any materials."
|
||||
));
|
||||
|
||||
if (shouldEditMaterial)
|
||||
{
|
||||
@ -255,6 +277,19 @@ namespace NiloToon.NiloToonURP
|
||||
|
||||
//Debug.Log($"NiloToon: Auto setup {perCharScript.gameObject.name} completed.");
|
||||
}
|
||||
|
||||
static bool ResolveShouldEditMaterialDecision(
|
||||
bool shouldPromptMaterialEditConfirmation,
|
||||
bool shouldEditMaterialWhenConfirmationSkipped,
|
||||
Func<bool> showConfirmationDialog)
|
||||
{
|
||||
if (!shouldPromptMaterialEditConfirmation)
|
||||
{
|
||||
return shouldEditMaterialWhenConfirmationSkipped;
|
||||
}
|
||||
|
||||
return showConfirmationDialog();
|
||||
}
|
||||
|
||||
static void DiscardAndRefillAllRenderersList(NiloToonPerCharacterRenderController t)
|
||||
{
|
||||
@ -414,4 +449,3 @@ namespace NiloToon.NiloToonURP
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// using System.Collections.Generic;
|
||||
// using System.Collections.Generic;
|
||||
// using System.IO;
|
||||
// using UnityEditor;
|
||||
// using UnityEditor.Build;
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
// Copyright (c) Jason Ma
|
||||
using System;
|
||||
using System.IO;
|
||||
using LWGUI.PerformanceMonitor;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
@ -15,10 +16,14 @@ namespace LWGUI
|
||||
{
|
||||
foreach (var assetPath in importedAssets)
|
||||
{
|
||||
if (Path.GetExtension(assetPath).Equals(".shader", StringComparison.OrdinalIgnoreCase))
|
||||
var ext = Path.GetExtension(assetPath);
|
||||
if (ext.Equals(".shader", StringComparison.OrdinalIgnoreCase)
|
||||
|| ext.Equals(".shadergraph", StringComparison.OrdinalIgnoreCase)
|
||||
)
|
||||
{
|
||||
var shader = AssetDatabase.LoadAssetAtPath<Shader>(assetPath);
|
||||
MetaDataHelper.ReleaseShaderMetadataCache(shader);
|
||||
ShaderPerfMonitor.ClearShaderPerfCache(shader);
|
||||
ReflectionHelper.InvalidatePropertyCache(shader);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace LWGUI.CustomGUISample
|
||||
|
||||
@ -0,0 +1,308 @@
|
||||
// Copyright (c) Jason Ma
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace LWGUI
|
||||
{
|
||||
public class ContextMenuHelper
|
||||
{
|
||||
#region Copy and Paste
|
||||
|
||||
private static Material _copiedMaterial;
|
||||
private static List<string> _copiedProps = new();
|
||||
|
||||
public static void CopyMaterial(Material mat)
|
||||
{
|
||||
_copiedMaterial = Object.Instantiate(mat);
|
||||
}
|
||||
|
||||
public static void PastePropertiesToMaterials(LWGUIMetaDatas metaDatas, uint valueMask)
|
||||
{
|
||||
if (!_copiedMaterial)
|
||||
{
|
||||
Debug.LogError("LWGUI: Please copy Material Properties first!");
|
||||
return;
|
||||
}
|
||||
|
||||
var targetMaterials = metaDatas.GetMaterialEditor().targets;
|
||||
if (!VersionControlHelper.Checkout(targetMaterials))
|
||||
{
|
||||
Debug.LogError("LWGUI: One or more materials unable to write!");
|
||||
return;
|
||||
}
|
||||
|
||||
Undo.RecordObjects(targetMaterials, "LWGUI: Paste Material Properties");
|
||||
foreach (Material material in targetMaterials)
|
||||
{
|
||||
PastePropertiesToMaterial(material, valueMask);
|
||||
}
|
||||
}
|
||||
|
||||
public static void PastePropertiesToMaterial(Material target, uint valueMask = ToolbarHelper.CopyMaterialValueMaskAll)
|
||||
{
|
||||
if (!_copiedMaterial)
|
||||
{
|
||||
Debug.LogError("LWGUI: Please copy Material Properties first!");
|
||||
return;
|
||||
}
|
||||
if (!VersionControlHelper.Checkout(target))
|
||||
{
|
||||
Debug.LogError("LWGUI: Unable to write material!");
|
||||
return;
|
||||
}
|
||||
Undo.RecordObject(target, "LWGUI: Paste Material Properties");
|
||||
for (int i = 0; i < _copiedMaterial.shader.GetPropertyCount(); i++)
|
||||
{
|
||||
var name = _copiedMaterial.shader.GetPropertyName(i);
|
||||
var type = _copiedMaterial.shader.GetPropertyType(i);
|
||||
PastePropertyValueToMaterial(target, name, name, type, valueMask);
|
||||
}
|
||||
if ((valueMask & (uint)ToolbarHelper.CopyMaterialValueMask.Keyword) != 0)
|
||||
target.shaderKeywords = _copiedMaterial.shaderKeywords;
|
||||
if ((valueMask & (uint)ToolbarHelper.CopyMaterialValueMask.RenderQueue) != 0)
|
||||
target.renderQueue = _copiedMaterial.renderQueue;
|
||||
}
|
||||
|
||||
private static void PastePropertyValueToMaterial(Material material, string srcName, string dstName)
|
||||
{
|
||||
for (int i = 0; i < _copiedMaterial.shader.GetPropertyCount(); i++)
|
||||
{
|
||||
var name = _copiedMaterial.shader.GetPropertyName(i);
|
||||
if (name == srcName)
|
||||
{
|
||||
var type = _copiedMaterial.shader.GetPropertyType(i);
|
||||
PastePropertyValueToMaterial(material, srcName, dstName, type);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void PastePropertyValueToMaterial(Material material, string srcName, string dstName, ShaderPropertyType type, uint valueMask = (uint)ToolbarHelper.CopyMaterialValueMask.All)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ShaderPropertyType.Color:
|
||||
if ((valueMask & (uint)ToolbarHelper.CopyMaterialValueMask.Vector) != 0)
|
||||
material.SetColor(dstName, _copiedMaterial.GetColor(srcName));
|
||||
break;
|
||||
case ShaderPropertyType.Vector:
|
||||
if ((valueMask & (uint)ToolbarHelper.CopyMaterialValueMask.Vector) != 0)
|
||||
material.SetVector(dstName, _copiedMaterial.GetVector(srcName));
|
||||
break;
|
||||
case ShaderPropertyType.Texture:
|
||||
if ((valueMask & (uint)ToolbarHelper.CopyMaterialValueMask.Texture) != 0)
|
||||
material.SetTexture(dstName, _copiedMaterial.GetTexture(srcName));
|
||||
break;
|
||||
// Float
|
||||
default:
|
||||
if ((valueMask & (uint)ToolbarHelper.CopyMaterialValueMask.Float) != 0)
|
||||
material.SetFloat(dstName, _copiedMaterial.GetFloat(srcName));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private static void EditPresetEvent(string mode, LwguiShaderPropertyPreset presetAsset, List<LwguiShaderPropertyPreset.Preset> targetPresets, MaterialProperty prop, LWGUIMetaDatas metaDatas)
|
||||
{
|
||||
if (!VersionControlHelper.Checkout(presetAsset))
|
||||
{
|
||||
Debug.LogError("LWGUI: Can not edit the preset: " + presetAsset);
|
||||
return;
|
||||
}
|
||||
foreach (var targetPreset in targetPresets)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case "Add":
|
||||
case "Update":
|
||||
targetPreset.AddOrUpdateIncludeExtraProperties(metaDatas, prop);
|
||||
break;
|
||||
case "Remove":
|
||||
targetPreset.RemoveIncludeExtraProperties(metaDatas, prop.name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
EditorUtility.SetDirty(presetAsset);
|
||||
metaDatas.perMaterialData.InvalidateDefaultMaterialCache();
|
||||
MetaDataHelper.ForceUpdateMaterialMetadataCache(metaDatas.GetMaterial());
|
||||
}
|
||||
|
||||
public static void DoPropertyContextMenus(Rect rect, MaterialProperty prop, LWGUIMetaDatas metaDatas)
|
||||
{
|
||||
if (Event.current.type != EventType.ContextClick || !rect.Contains(Event.current.mousePosition)) return;
|
||||
|
||||
Event.current.Use();
|
||||
|
||||
var (perShaderData, perMaterialData, perInspectorData) = metaDatas.GetDatas();
|
||||
var (propStaticData, propDynamicData) = metaDatas.GetPropDatas(prop);
|
||||
var menu = new GenericMenu();
|
||||
|
||||
// 2022+ Material Varant Menus
|
||||
#if UNITY_2022_1_OR_NEWER
|
||||
ReflectionHelper.HandleApplyRevert(menu, prop);
|
||||
#endif
|
||||
|
||||
// Copy
|
||||
menu.AddItem(new GUIContent("Copy"), false, () =>
|
||||
{
|
||||
_copiedMaterial = UnityEngine.Object.Instantiate(metaDatas.GetMaterial());
|
||||
_copiedProps.Clear();
|
||||
_copiedProps.Add(prop.name);
|
||||
foreach (var extraPropName in propStaticData.extraPropNames)
|
||||
{
|
||||
_copiedProps.Add(extraPropName);
|
||||
}
|
||||
|
||||
// Copy Children
|
||||
foreach (var childPropStaticData in propStaticData.children)
|
||||
{
|
||||
_copiedProps.Add(childPropStaticData.name);
|
||||
foreach (var extraPropName in childPropStaticData.extraPropNames)
|
||||
{
|
||||
_copiedProps.Add(extraPropName);
|
||||
}
|
||||
|
||||
foreach (var childChildPropStaticData in childPropStaticData.children)
|
||||
{
|
||||
_copiedProps.Add(childChildPropStaticData.name);
|
||||
foreach (var extraPropName in childChildPropStaticData.extraPropNames)
|
||||
{
|
||||
_copiedProps.Add(extraPropName);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Paste
|
||||
GenericMenu.MenuFunction pasteAction = () =>
|
||||
{
|
||||
if (!VersionControlHelper.Checkout(prop.targets))
|
||||
{
|
||||
Debug.LogError("LWGUI: One or more materials unable to write!");
|
||||
return;
|
||||
}
|
||||
|
||||
Undo.RecordObjects(prop.targets, "LWGUI: Paste Material Properties");
|
||||
|
||||
foreach (Material material in prop.targets)
|
||||
{
|
||||
var index = 0;
|
||||
|
||||
PastePropertyValueToMaterial(material, _copiedProps[index++], prop.name);
|
||||
foreach (var extraPropName in propStaticData.extraPropNames)
|
||||
{
|
||||
if (index == _copiedProps.Count) break;
|
||||
PastePropertyValueToMaterial(material, _copiedProps[index++], extraPropName);
|
||||
}
|
||||
|
||||
// Paste Children
|
||||
foreach (var childPropStaticData in propStaticData.children)
|
||||
{
|
||||
if (index == _copiedProps.Count) break;
|
||||
PastePropertyValueToMaterial(material, _copiedProps[index++], childPropStaticData.name);
|
||||
foreach (var extraPropName in childPropStaticData.extraPropNames)
|
||||
{
|
||||
if (index == _copiedProps.Count) break;
|
||||
PastePropertyValueToMaterial(material, _copiedProps[index++], extraPropName);
|
||||
}
|
||||
|
||||
foreach (var childChildPropStaticData in childPropStaticData.children)
|
||||
{
|
||||
if (index == _copiedProps.Count) break;
|
||||
PastePropertyValueToMaterial(material, _copiedProps[index++], childChildPropStaticData.name);
|
||||
foreach (var extraPropName in childChildPropStaticData.extraPropNames)
|
||||
{
|
||||
if (index == _copiedProps.Count) break;
|
||||
PastePropertyValueToMaterial(material, _copiedProps[index++], extraPropName);
|
||||
}
|
||||
}
|
||||
}
|
||||
MetaDataHelper.ForceUpdateMaterialMetadataCache(material);
|
||||
}
|
||||
};
|
||||
|
||||
if (_copiedMaterial != null && _copiedProps.Count > 0 && GUI.enabled)
|
||||
menu.AddItem(new GUIContent("Paste"), false, pasteAction);
|
||||
else
|
||||
menu.AddDisabledItem(new GUIContent("Paste"));
|
||||
|
||||
menu.AddSeparator("");
|
||||
|
||||
// Copy Display Name
|
||||
menu.AddItem(new GUIContent("Copy Display Name"), false, () =>
|
||||
{
|
||||
EditorGUIUtility.systemCopyBuffer = propStaticData.displayName;
|
||||
});
|
||||
|
||||
// Copy Property Names
|
||||
menu.AddItem(new GUIContent("Copy Property Names"), false, () =>
|
||||
{
|
||||
EditorGUIUtility.systemCopyBuffer = prop.name;
|
||||
foreach (var extraPropName in propStaticData.extraPropNames)
|
||||
{
|
||||
EditorGUIUtility.systemCopyBuffer += ", " + extraPropName;
|
||||
}
|
||||
});
|
||||
|
||||
// menus.AddSeparator("");
|
||||
//
|
||||
// // Add to Favorites
|
||||
// menus.AddItem(new GUIContent("Add to Favorites"), false, () =>
|
||||
// {
|
||||
// });
|
||||
//
|
||||
// // Remove from Favorites
|
||||
// menus.AddItem(new GUIContent("Remove from Favorites"), false, () =>
|
||||
// {
|
||||
// });
|
||||
|
||||
// Preset
|
||||
if (GUI.enabled)
|
||||
{
|
||||
menu.AddSeparator("");
|
||||
foreach (var activePresetData in perMaterialData.activePresetDatas)
|
||||
{
|
||||
// Cull self
|
||||
if (activePresetData.property == prop) continue;
|
||||
|
||||
var activePreset = activePresetData.preset;
|
||||
var (presetPropStaticData, presetPropDynamicData) = metaDatas.GetPropDatas(activePresetData.property);
|
||||
var presetAsset = presetPropStaticData.propertyPresetAsset;
|
||||
var presetPropDisplayName = presetPropStaticData.displayName;
|
||||
|
||||
// Cull invisible presets
|
||||
if (!presetPropDynamicData.isShowing) continue;
|
||||
|
||||
if (activePreset.GetPropertyValue(prop.name) != null)
|
||||
{
|
||||
menu.AddItem(new GUIContent("Update to Preset/" + presetPropDisplayName + "/" + "All"), false, () => EditPresetEvent("Update", presetAsset, presetAsset.GetPresets(), prop, metaDatas));
|
||||
menu.AddItem(new GUIContent("Update to Preset/" + presetPropDisplayName + "/" + activePreset.presetName), false, () => EditPresetEvent("Update", presetAsset, new List<LwguiShaderPropertyPreset.Preset>(){activePreset}, prop, metaDatas));
|
||||
menu.AddItem(new GUIContent("Remove from Preset/" + presetPropDisplayName + "/" + "All"), false, () => EditPresetEvent("Remove", presetAsset, presetAsset.GetPresets(), prop, metaDatas));
|
||||
menu.AddItem(new GUIContent("Remove from Preset/" + presetPropDisplayName + "/" + activePreset.presetName), false, () => EditPresetEvent("Remove", presetAsset, new List<LwguiShaderPropertyPreset.Preset>(){activePreset}, prop, metaDatas));
|
||||
}
|
||||
else
|
||||
{
|
||||
menu.AddItem(new GUIContent("Add to Preset/" + presetPropDisplayName + "/" + "All"), false, () => EditPresetEvent("Add", presetAsset, presetAsset.GetPresets(), prop, metaDatas));
|
||||
menu.AddItem(new GUIContent("Add to Preset/" + presetPropDisplayName + "/" + activePreset.presetName), false, () => EditPresetEvent("Add", presetAsset, new List<LwguiShaderPropertyPreset.Preset>(){activePreset}, prop, metaDatas));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Custom
|
||||
if (propStaticData.baseDrawers != null)
|
||||
{
|
||||
foreach (var baseDrawer in propStaticData.baseDrawers)
|
||||
{
|
||||
baseDrawer.GetCustomContextMenus(menu, rect, prop, metaDatas);
|
||||
}
|
||||
}
|
||||
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aedae295d18e4884bc12b86aadfbab80
|
||||
timeCreated: 1760781897
|
||||
@ -0,0 +1,84 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace LWGUI
|
||||
{
|
||||
public static class GUIStyles
|
||||
{
|
||||
// Tips: Use properties to fix null reference errors
|
||||
private static GUIStyle _title;
|
||||
private static GUIStyle _iconButton;
|
||||
private static GUIStyle _foldout;
|
||||
private static GUIStyle _helpbox;
|
||||
private static GUIStyle _rampSelectButton;
|
||||
private static GUIStyle _objectFieldButton;
|
||||
private static GUIStyle _toolbarSearchTextFieldPopup;
|
||||
private static GUIStyle _label_monospace;
|
||||
|
||||
|
||||
public static GUIStyle title => _title ?? new GUIStyle(EditorStyles.boldLabel)
|
||||
{
|
||||
alignment = TextAnchor.LowerLeft,
|
||||
border =
|
||||
{
|
||||
bottom = 2
|
||||
}
|
||||
};
|
||||
|
||||
public static GUIStyle iconButton => _iconButton ?? new GUIStyle(EditorStyles.iconButton) { fixedHeight = 0, fixedWidth = 0 };
|
||||
|
||||
public static GUIStyle foldout => _foldout ?? new GUIStyle(EditorStyles.miniButton)
|
||||
{
|
||||
contentOffset = new Vector2(22, 0),
|
||||
fixedHeight = 27,
|
||||
alignment = TextAnchor.MiddleLeft,
|
||||
font = EditorStyles.boldLabel.font,
|
||||
fontSize = EditorStyles.boldLabel.fontSize + 1
|
||||
};
|
||||
|
||||
public static GUIStyle helpbox => _helpbox ?? new GUIStyle(EditorStyles.helpBox) { fontSize = 12 };
|
||||
|
||||
public static GUIStyle rampSelectButton => _rampSelectButton ?? new GUIStyle(EditorStyles.miniButton)
|
||||
{
|
||||
fixedHeight = 0,
|
||||
stretchHeight = true,
|
||||
alignment = TextAnchor.MiddleLeft
|
||||
};
|
||||
|
||||
public static GUIStyle objectFieldButton => _objectFieldButton ?? new GUIStyle("ObjectFieldButton")
|
||||
{
|
||||
fixedHeight = 0,
|
||||
stretchHeight = true
|
||||
};
|
||||
|
||||
public static GUIStyle toolbarSearchTextFieldPopup
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_toolbarSearchTextFieldPopup == null)
|
||||
{
|
||||
string toolbarSeachTextFieldPopupStr = "ToolbarSeachTextFieldPopup";
|
||||
{
|
||||
// ToolbarSeachTextFieldPopup has renamed at Unity 2021.3.28+
|
||||
#if !UNITY_2022_3_OR_NEWER
|
||||
string[] versionParts = Application.unityVersion.Split('.');
|
||||
int majorVersion = int.Parse(versionParts[0]);
|
||||
int minorVersion = int.Parse(versionParts[1]);
|
||||
Match patchVersionMatch = Regex.Match(versionParts[2], @"\d+");
|
||||
int patchVersion = int.Parse(patchVersionMatch.Value);
|
||||
if (majorVersion >= 2021 && minorVersion >= 3 && patchVersion >= 28)
|
||||
#endif
|
||||
{
|
||||
toolbarSeachTextFieldPopupStr = "ToolbarSearchTextFieldPopup";
|
||||
}
|
||||
}
|
||||
_toolbarSearchTextFieldPopup = new GUIStyle(toolbarSeachTextFieldPopupStr);
|
||||
}
|
||||
return _toolbarSearchTextFieldPopup;
|
||||
}
|
||||
}
|
||||
|
||||
public static GUIStyle label_monospace => _label_monospace ?? new GUIStyle(EditorStyles.label) { font = EditorGUIUtility.Load("Fonts/RobotoMono/RobotoMono-Regular.ttf") as Font };
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 09e9a4a53c4e4d36b20c466825f07a63
|
||||
timeCreated: 1760968069
|
||||
@ -20,29 +20,11 @@ namespace LWGUI
|
||||
|
||||
#region Misc
|
||||
|
||||
public static readonly string ProjectPath = Application.dataPath.Substring(0, Application.dataPath.Length - 6);
|
||||
|
||||
public static bool IsPropertyHideInInspector(MaterialProperty prop)
|
||||
{
|
||||
return (prop.GetPropertyFlags() & ShaderPropertyFlags.HideInInspector) != 0;
|
||||
}
|
||||
|
||||
public static bool StringToBool(string str) => str?.ToLower() is "on" or "true";
|
||||
|
||||
public static string GetKeywordName(string keyword, string propName)
|
||||
{
|
||||
string k;
|
||||
if (string.IsNullOrEmpty(keyword) || keyword == "__")
|
||||
{
|
||||
k = propName.ToUpperInvariant() + "_ON";
|
||||
}
|
||||
else
|
||||
{
|
||||
k = keyword.ToUpperInvariant();
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
public static void SetShaderKeywordEnabled(Object[] materials, string keywordName, bool isEnable)
|
||||
{
|
||||
if (string.IsNullOrEmpty(keywordName) || string.IsNullOrEmpty(keywordName)) return;
|
||||
@ -90,6 +72,7 @@ namespace LWGUI
|
||||
{
|
||||
material.SetShaderPassEnabled(lightModeNames[i], enabled);
|
||||
}
|
||||
EditorUtility.SetDirty(material);
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,13 +93,6 @@ namespace LWGUI
|
||||
|
||||
public static LWGUIMetaDatas GetLWGUIMetadatas(MaterialEditor editor) => GetLWGUI(editor).metaDatas;
|
||||
|
||||
public static void AdaptiveFieldWidth(GUIStyle style, GUIContent content)
|
||||
{
|
||||
var extraTextWidth = Mathf.Max(0, style.CalcSize(content).x - (EditorGUIUtility.fieldWidth - RevertableHelper.revertButtonWidth));
|
||||
EditorGUIUtility.labelWidth -= extraTextWidth;
|
||||
EditorGUIUtility.fieldWidth += extraTextWidth;
|
||||
}
|
||||
|
||||
public static void BeginProperty(Rect rect, MaterialProperty property, LWGUIMetaDatas metaDatas)
|
||||
{
|
||||
#if UNITY_2022_1_OR_NEWER
|
||||
@ -148,6 +124,43 @@ namespace LWGUI
|
||||
#endregion
|
||||
|
||||
|
||||
#region String
|
||||
|
||||
public static bool StringToBool(string str) => str?.ToLower() is "on" or "true";
|
||||
|
||||
public static string FillStringLengthBySpace(string str, int minStringLength)
|
||||
{
|
||||
if (str.Length >= minStringLength)
|
||||
return str;
|
||||
|
||||
return str + string.Concat(Enumerable.Repeat(' ', minStringLength - str.Length));
|
||||
}
|
||||
|
||||
public static string GetKeywordName(string keyword, string propName)
|
||||
{
|
||||
string k;
|
||||
if (string.IsNullOrEmpty(keyword) || keyword == "__")
|
||||
{
|
||||
k = propName.ToUpperInvariant() + "_ON";
|
||||
}
|
||||
else
|
||||
{
|
||||
k = keyword.ToUpperInvariant();
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
public static void AdaptiveFieldWidth(GUIStyle style, GUIContent content)
|
||||
{
|
||||
var extraTextWidth = Mathf.Max(0, style.CalcSize(content).x - (EditorGUIUtility.fieldWidth - RevertableHelper.revertButtonWidth));
|
||||
EditorGUIUtility.labelWidth -= extraTextWidth;
|
||||
EditorGUIUtility.fieldWidth += extraTextWidth;
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Math
|
||||
|
||||
public const double Float_Epsilon = 1e-10;
|
||||
@ -178,60 +191,6 @@ namespace LWGUI
|
||||
#endregion
|
||||
|
||||
|
||||
#region GUI Styles
|
||||
|
||||
// Tips: Use properties to fix null reference errors
|
||||
|
||||
private static GUIStyle _guiStyles_IconButton;
|
||||
public static GUIStyle guiStyles_IconButton => _guiStyles_IconButton ?? new GUIStyle(EditorStyles.iconButton) { fixedHeight = 0, fixedWidth = 0 };
|
||||
|
||||
private static GUIStyle _guiStyle_Foldout;
|
||||
public static GUIStyle guiStyle_Foldout => _guiStyle_Foldout ?? new GUIStyle(EditorStyles.miniButton)
|
||||
{
|
||||
contentOffset = new Vector2(22, 0),
|
||||
fixedHeight = 27,
|
||||
alignment = TextAnchor.MiddleLeft,
|
||||
font = EditorStyles.boldLabel.font,
|
||||
fontSize = EditorStyles.boldLabel.fontSize + 1
|
||||
};
|
||||
|
||||
private static GUIStyle _guiStyle_Helpbox;
|
||||
public static GUIStyle guiStyle_Helpbox => _guiStyle_Helpbox ?? new GUIStyle(EditorStyles.helpBox) { fontSize = 12 };
|
||||
|
||||
private static GUIStyle _guiStyle_RampSelectButton;
|
||||
public static GUIStyle guiStyle_RampSelectButton => _guiStyle_RampSelectButton ?? new GUIStyle(EditorStyles.miniButton) { alignment = TextAnchor.MiddleLeft };
|
||||
|
||||
private static GUIStyle _guiStyles_ToolbarSearchTextFieldPopup;
|
||||
public static GUIStyle guiStyles_ToolbarSearchTextFieldPopup
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_guiStyles_ToolbarSearchTextFieldPopup == null)
|
||||
{
|
||||
string toolbarSeachTextFieldPopupStr = "ToolbarSeachTextFieldPopup";
|
||||
{
|
||||
// ToolbarSeachTextFieldPopup has renamed at Unity 2021.3.28+
|
||||
#if !UNITY_2022_3_OR_NEWER
|
||||
string[] versionParts = Application.unityVersion.Split('.');
|
||||
int majorVersion = int.Parse(versionParts[0]);
|
||||
int minorVersion = int.Parse(versionParts[1]);
|
||||
Match patchVersionMatch = Regex.Match(versionParts[2], @"\d+");
|
||||
int patchVersion = int.Parse(patchVersionMatch.Value);
|
||||
if (majorVersion >= 2021 && minorVersion >= 3 && patchVersion >= 28)
|
||||
#endif
|
||||
{
|
||||
toolbarSeachTextFieldPopupStr = "ToolbarSearchTextFieldPopup";
|
||||
}
|
||||
}
|
||||
_guiStyles_ToolbarSearchTextFieldPopup = new GUIStyle(toolbarSeachTextFieldPopupStr);
|
||||
}
|
||||
return _guiStyles_ToolbarSearchTextFieldPopup;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Draw GUI for Drawers
|
||||
|
||||
// TODO: use Reflection
|
||||
@ -285,7 +244,7 @@ namespace LWGUI
|
||||
GUI.enabled = true;
|
||||
var guiColor = GUI.backgroundColor;
|
||||
GUI.backgroundColor = isFolding ? Color.white : new Color(0.85f, 0.85f, 0.85f);
|
||||
if (GUI.Button(rect, label, guiStyle_Foldout))
|
||||
if (GUI.Button(rect, label, GUIStyles.foldout))
|
||||
{
|
||||
isFolding = !isFolding;
|
||||
GUI.changed = false;
|
||||
@ -349,10 +308,10 @@ namespace LWGUI
|
||||
{
|
||||
var content = new GUIContent(helpboxStr, _helpboxIcon);
|
||||
var textWidth = GetCurrentPropertyLayoutWidth();
|
||||
var textHeight = guiStyle_Helpbox.CalcHeight(content, textWidth);
|
||||
var textHeight = GUIStyles.helpbox.CalcHeight(content, textWidth);
|
||||
var helpboxRect = EditorGUI.IndentedRect(EditorGUILayout.GetControlRect(true, textHeight));
|
||||
helpboxRect.xMax -= RevertableHelper.revertButtonWidth;
|
||||
GUI.Label(helpboxRect, content, guiStyle_Helpbox);
|
||||
GUI.Label(helpboxRect, content, GUIStyles.helpbox);
|
||||
// EditorGUI.HelpBox(helpboxRect, helpboxStr, MessageType.Info);
|
||||
}
|
||||
}
|
||||
@ -372,610 +331,20 @@ namespace LWGUI
|
||||
logoRect.xMin += w * 0.5f - _logo.width * 0.5f;
|
||||
logoRect.xMax -= w * 0.5f - _logo.width * 0.5f;
|
||||
|
||||
if (EditorGUIUtility.currentViewWidth >= logoRect.width && GUI.Button(logoRect, _logoGuiContent, guiStyles_IconButton))
|
||||
if (EditorGUIUtility.currentViewWidth >= logoRect.width && GUI.Button(logoRect, _logoGuiContent, GUIStyles.iconButton))
|
||||
{
|
||||
Application.OpenURL("https://github.com/JasonMa0012/LWGUI");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Toolbar Buttons
|
||||
|
||||
private static Material _copiedMaterial;
|
||||
private static List<string> _copiedProps = new List<string>();
|
||||
|
||||
private const string _iconCopyGUID = "9cdef444d18d2ce4abb6bbc4fed4d109";
|
||||
private const string _iconPasteGUID = "8e7a78d02e4c3574998524a0842a8ccb";
|
||||
private const string _iconSelectGUID = "6f44e40b24300974eb607293e4224ecc";
|
||||
private const string _iconCheckoutGUID = "72488141525eaa8499e65e52755cb6d0";
|
||||
private const string _iconExpandGUID = "2382450e7f4ddb94c9180d6634c41378";
|
||||
private const string _iconCollapseGUID = "929b6e5dfacc42b429d715a3e1ca2b57";
|
||||
private const string _iconVisibilityGUID = "9576e23a695b35d49a9fc55c9a948b4f";
|
||||
|
||||
private const string _iconCopyTooltip = "Copy Material Properties";
|
||||
private const string _iconPasteTooltip = "Paste Material Properties\n\nRight-click to paste values by type.";
|
||||
private const string _iconSelectTooltip = "Select the Material Asset\n\nUsed to jump from a Runtime Material Instance to a Material Asset.";
|
||||
private const string _iconCheckoutTooltip = "Checkout selected Material Assets";
|
||||
private const string _iconExpandTooltip = "Expand All Groups";
|
||||
private const string _iconCollapseTooltip = "Collapse All Groups";
|
||||
private const string _iconVisibilityTooltip = "Display Mode";
|
||||
|
||||
private static GUIContent _guiContentCopyCache;
|
||||
private static GUIContent _guiContentPasteCache;
|
||||
private static GUIContent _guiContentSelectCache;
|
||||
private static GUIContent _guiContentChechoutCache;
|
||||
private static GUIContent _guiContentExpandCache;
|
||||
private static GUIContent _guiContentCollapseCache;
|
||||
private static GUIContent _guiContentVisibilityCache;
|
||||
|
||||
private static GUIContent _guiContentCopy => _guiContentCopyCache = _guiContentCopyCache ?? new GUIContent("", AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath(_iconCopyGUID)), _iconCopyTooltip);
|
||||
private static GUIContent _guiContentPaste => _guiContentPasteCache = _guiContentPasteCache ?? new GUIContent("", AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath(_iconPasteGUID)), _iconPasteTooltip);
|
||||
private static GUIContent _guiContentSelect => _guiContentSelectCache = _guiContentSelectCache ?? new GUIContent("", AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath(_iconSelectGUID)), _iconSelectTooltip);
|
||||
private static GUIContent _guiContentChechout => _guiContentChechoutCache = _guiContentChechoutCache ?? new GUIContent("", AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath(_iconCheckoutGUID)), _iconCheckoutTooltip);
|
||||
private static GUIContent _guiContentExpand => _guiContentExpandCache = _guiContentExpandCache ?? new GUIContent("", AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath(_iconExpandGUID)), _iconExpandTooltip);
|
||||
private static GUIContent _guiContentCollapse => _guiContentCollapseCache = _guiContentCollapseCache ?? new GUIContent("", AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath(_iconCollapseGUID)), _iconCollapseTooltip);
|
||||
private static GUIContent _guiContentVisibility => _guiContentVisibilityCache = _guiContentVisibilityCache ?? new GUIContent("", AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath(_iconVisibilityGUID)), _iconVisibilityTooltip);
|
||||
|
||||
|
||||
private enum CopyMaterialValueMask
|
||||
{
|
||||
Float = 1 << 0,
|
||||
Vector = 1 << 1,
|
||||
Texture = 1 << 2,
|
||||
Keyword = 1 << 3,
|
||||
RenderQueue = 1 << 4,
|
||||
Number = Float | Vector,
|
||||
All = (1 << 5) - 1,
|
||||
}
|
||||
|
||||
private static GUIContent[] _pasteMaterialMenus = new[]
|
||||
{
|
||||
new GUIContent("Paste Number Values"),
|
||||
new GUIContent("Paste Texture Values"),
|
||||
new GUIContent("Paste Keywords"),
|
||||
new GUIContent("Paste RenderQueue"),
|
||||
};
|
||||
|
||||
private static uint[] _pasteMaterialMenuValueMasks = new[]
|
||||
{
|
||||
(uint)CopyMaterialValueMask.Number,
|
||||
(uint)CopyMaterialValueMask.Texture,
|
||||
(uint)CopyMaterialValueMask.Keyword,
|
||||
(uint)CopyMaterialValueMask.RenderQueue,
|
||||
};
|
||||
|
||||
private static void DoPasteMaterialProperties(LWGUIMetaDatas metaDatas, uint valueMask)
|
||||
{
|
||||
if (!_copiedMaterial)
|
||||
{
|
||||
Debug.LogError("LWGUI: Please copy Material Properties first!");
|
||||
return;
|
||||
}
|
||||
|
||||
var targetMaterials = metaDatas.GetMaterialEditor().targets;
|
||||
if (!VersionControlHelper.Checkout(targetMaterials))
|
||||
{
|
||||
Debug.LogError("LWGUI: One or more materials unable to write!");
|
||||
return;
|
||||
}
|
||||
|
||||
Undo.RecordObjects(targetMaterials, "LWGUI: Paste Material Properties");
|
||||
foreach (Material material in targetMaterials)
|
||||
{
|
||||
for (int i = 0; i < _copiedMaterial.shader.GetPropertyCount(); i++)
|
||||
{
|
||||
var name = _copiedMaterial.shader.GetPropertyName(i);
|
||||
var type = _copiedMaterial.shader.GetPropertyType(i);
|
||||
PastePropertyValueToMaterial(material, name, name, type, valueMask);
|
||||
}
|
||||
if ((valueMask & (uint)CopyMaterialValueMask.Keyword) != 0)
|
||||
material.shaderKeywords = _copiedMaterial.shaderKeywords;
|
||||
if ((valueMask & (uint)CopyMaterialValueMask.RenderQueue) != 0)
|
||||
material.renderQueue = _copiedMaterial.renderQueue;
|
||||
}
|
||||
}
|
||||
|
||||
private static void PastePropertyValueToMaterial(Material material, string srcName, string dstName)
|
||||
{
|
||||
for (int i = 0; i < _copiedMaterial.shader.GetPropertyCount(); i++)
|
||||
{
|
||||
var name = _copiedMaterial.shader.GetPropertyName(i);
|
||||
if (name == srcName)
|
||||
{
|
||||
var type = _copiedMaterial.shader.GetPropertyType(i);
|
||||
PastePropertyValueToMaterial(material, srcName, dstName, type);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void PastePropertyValueToMaterial(Material material, string srcName, string dstName, ShaderPropertyType type, uint valueMask = (uint)CopyMaterialValueMask.All)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ShaderPropertyType.Color:
|
||||
if ((valueMask & (uint)CopyMaterialValueMask.Vector) != 0)
|
||||
material.SetColor(dstName, _copiedMaterial.GetColor(srcName));
|
||||
break;
|
||||
case ShaderPropertyType.Vector:
|
||||
if ((valueMask & (uint)CopyMaterialValueMask.Vector) != 0)
|
||||
material.SetVector(dstName, _copiedMaterial.GetVector(srcName));
|
||||
break;
|
||||
case ShaderPropertyType.Texture:
|
||||
if ((valueMask & (uint)CopyMaterialValueMask.Texture) != 0)
|
||||
material.SetTexture(dstName, _copiedMaterial.GetTexture(srcName));
|
||||
break;
|
||||
// Float
|
||||
default:
|
||||
if ((valueMask & (uint)CopyMaterialValueMask.Float) != 0)
|
||||
material.SetFloat(dstName, _copiedMaterial.GetFloat(srcName));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawToolbarButtons(ref Rect toolBarRect, LWGUIMetaDatas metaDatas)
|
||||
{
|
||||
var (perShaderData, perMaterialData, perInspectorData) = metaDatas.GetDatas();
|
||||
|
||||
// Copy
|
||||
var buttonRectOffset = toolBarRect.height + 2;
|
||||
var buttonRect = new Rect(toolBarRect.x, toolBarRect.y, toolBarRect.height, toolBarRect.height);
|
||||
toolBarRect.xMin += buttonRectOffset;
|
||||
if (GUI.Button(buttonRect, _guiContentCopy, Helper.guiStyles_IconButton))
|
||||
{
|
||||
_copiedMaterial = UnityEngine.Object.Instantiate(metaDatas.GetMaterial());
|
||||
}
|
||||
|
||||
// Paste
|
||||
buttonRect.x += buttonRectOffset;
|
||||
toolBarRect.xMin += buttonRectOffset;
|
||||
// Right Click
|
||||
if (Event.current.type == EventType.MouseDown
|
||||
&& Event.current.button == 1
|
||||
&& buttonRect.Contains(Event.current.mousePosition))
|
||||
{
|
||||
EditorUtility.DisplayCustomMenu(new Rect(Event.current.mousePosition.x, Event.current.mousePosition.y, 0, 0), _pasteMaterialMenus, -1,
|
||||
(data, options, selected) =>
|
||||
{
|
||||
DoPasteMaterialProperties(metaDatas, _pasteMaterialMenuValueMasks[selected]);
|
||||
}, null);
|
||||
Event.current.Use();
|
||||
}
|
||||
// Left Click
|
||||
if (GUI.Button(buttonRect, _guiContentPaste, Helper.guiStyles_IconButton))
|
||||
{
|
||||
DoPasteMaterialProperties(metaDatas, (uint)CopyMaterialValueMask.All);
|
||||
}
|
||||
|
||||
// Select Material Asset, jump from a Runtime Material Instance to a Material Asset
|
||||
buttonRect.x += buttonRectOffset;
|
||||
toolBarRect.xMin += buttonRectOffset;
|
||||
if (GUI.Button(buttonRect, _guiContentSelect, Helper.guiStyles_IconButton))
|
||||
{
|
||||
var material = metaDatas.GetMaterial();
|
||||
|
||||
if (AssetDatabase.Contains(material))
|
||||
{
|
||||
Selection.activeObject = material;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FindMaterialAssetByMaterialInstance(material, metaDatas, out var materialAsset))
|
||||
{
|
||||
Selection.activeObject = materialAsset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Checkout
|
||||
buttonRect.x += buttonRectOffset;
|
||||
toolBarRect.xMin += buttonRectOffset;
|
||||
if (GUI.Button(buttonRect, _guiContentChechout, Helper.guiStyles_IconButton))
|
||||
{
|
||||
VersionControlHelper.Checkout(metaDatas.GetMaterialEditor().targets);
|
||||
}
|
||||
|
||||
// Expand
|
||||
buttonRect.x += buttonRectOffset;
|
||||
toolBarRect.xMin += buttonRectOffset;
|
||||
if (GUI.Button(buttonRect, _guiContentExpand, Helper.guiStyles_IconButton))
|
||||
{
|
||||
foreach (var propStaticDataKVPair in perShaderData.propStaticDatas)
|
||||
{
|
||||
if (propStaticDataKVPair.Value.isMain || propStaticDataKVPair.Value.isAdvancedHeader)
|
||||
propStaticDataKVPair.Value.isExpanding = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Collapse
|
||||
buttonRect.x += buttonRectOffset;
|
||||
toolBarRect.xMin += buttonRectOffset;
|
||||
if (GUI.Button(buttonRect, _guiContentCollapse, Helper.guiStyles_IconButton))
|
||||
{
|
||||
foreach (var propStaticDataKVPair in perShaderData.propStaticDatas)
|
||||
{
|
||||
if (propStaticDataKVPair.Value.isMain || propStaticDataKVPair.Value.isAdvancedHeader)
|
||||
propStaticDataKVPair.Value.isExpanding = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Display Mode
|
||||
buttonRect.x += buttonRectOffset;
|
||||
toolBarRect.xMin += buttonRectOffset;
|
||||
var color = GUI.color;
|
||||
var displayModeData = perShaderData.displayModeData;
|
||||
if (!displayModeData.IsDefaultDisplayMode())
|
||||
GUI.color = Color.yellow;
|
||||
if (GUI.Button(buttonRect, _guiContentVisibility, Helper.guiStyles_IconButton))
|
||||
{
|
||||
// Build Display Mode Menu Items
|
||||
var displayModeMenus = new[]
|
||||
{
|
||||
$"Show All Advanced Properties ({ displayModeData.advancedCount } - { perShaderData.propStaticDatas.Count })",
|
||||
$"Show All Hidden Properties ({ displayModeData.hiddenCount } - { perShaderData.propStaticDatas.Count })",
|
||||
$"Show Only Modified Properties ({ perMaterialData.modifiedCount } - { perShaderData.propStaticDatas.Count })",
|
||||
$"Show Only Modified Properties by Group ({ perMaterialData.modifiedCount } - { perShaderData.propStaticDatas.Count })",
|
||||
};
|
||||
var enabled = new[] { true, true, true, true };
|
||||
var separator = new bool[4];
|
||||
var selected = new[]
|
||||
{
|
||||
displayModeData.showAllAdvancedProperties ? 0 : -1,
|
||||
displayModeData.showAllHiddenProperties ? 1 : -1,
|
||||
displayModeData.showOnlyModifiedProperties ? 2 : -1,
|
||||
displayModeData.showOnlyModifiedGroups ? 3 : -1,
|
||||
};
|
||||
|
||||
|
||||
// Click Event
|
||||
void OnSwitchDisplayMode(object data, string[] options, int selectedIndex)
|
||||
{
|
||||
switch (selectedIndex)
|
||||
{
|
||||
case 0: // Show All Advanced Properties
|
||||
displayModeData.showAllAdvancedProperties = !displayModeData.showAllAdvancedProperties;
|
||||
perShaderData.ToggleShowAllAdvancedProperties();
|
||||
break;
|
||||
case 1: // Show All Hidden Properties
|
||||
displayModeData.showAllHiddenProperties = !displayModeData.showAllHiddenProperties;
|
||||
break;
|
||||
case 2: // Show Only Modified Properties
|
||||
displayModeData.showOnlyModifiedProperties = !displayModeData.showOnlyModifiedProperties;
|
||||
if (displayModeData.showOnlyModifiedProperties) displayModeData.showOnlyModifiedGroups = false;
|
||||
MetaDataHelper.ForceUpdateAllMaterialsMetadataCache(metaDatas.GetShader());
|
||||
break;
|
||||
case 3: // Show Only Modified Groups
|
||||
displayModeData.showOnlyModifiedGroups = !displayModeData.showOnlyModifiedGroups;
|
||||
if (displayModeData.showOnlyModifiedGroups) displayModeData.showOnlyModifiedProperties = false;
|
||||
MetaDataHelper.ForceUpdateAllMaterialsMetadataCache(metaDatas.GetShader());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ReflectionHelper.DisplayCustomMenuWithSeparators(new Rect(Event.current.mousePosition.x, Event.current.mousePosition.y, 0, 0),
|
||||
displayModeMenus, enabled, separator, selected, OnSwitchDisplayMode);
|
||||
}
|
||||
GUI.color = color;
|
||||
|
||||
toolBarRect.xMin += 2;
|
||||
}
|
||||
|
||||
public static Func<Renderer, Material, Material> onFindMaterialAssetInRendererByMaterialInstance;
|
||||
|
||||
private static bool FindMaterialAssetByMaterialInstance(Material material, LWGUIMetaDatas metaDatas, out Material materialAsset)
|
||||
{
|
||||
materialAsset = null;
|
||||
|
||||
var renderers = metaDatas.perInspectorData.materialEditor.GetMeshRenderersByMaterialEditor();
|
||||
foreach (var renderer in renderers)
|
||||
{
|
||||
if (onFindMaterialAssetInRendererByMaterialInstance != null)
|
||||
{
|
||||
materialAsset = onFindMaterialAssetInRendererByMaterialInstance(renderer, material);
|
||||
}
|
||||
|
||||
if (materialAsset == null)
|
||||
{
|
||||
int index = renderer.materials.ToList().FindIndex(materialInstance => materialInstance == material);
|
||||
if (index >= 0 && index < renderer.sharedMaterials.Length)
|
||||
{
|
||||
materialAsset = renderer.sharedMaterials[index];
|
||||
}
|
||||
}
|
||||
|
||||
if (materialAsset != null && AssetDatabase.Contains(materialAsset))
|
||||
return true;
|
||||
}
|
||||
|
||||
Debug.LogError("LWGUI: Can not find the Material Assets of: " + material.name);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Search Field
|
||||
|
||||
private static readonly int s_TextFieldHash = "EditorTextField".GetHashCode();
|
||||
private static readonly GUIContent[] _searchModeMenus = Enumerable.Range(0, (int)SearchMode.Num - 1).Select(i =>
|
||||
new GUIContent(((SearchMode)i).ToString())).ToArray();
|
||||
|
||||
/// <returns>is has changed?</returns>
|
||||
public static bool DrawSearchField(Rect rect, LWGUIMetaDatas metaDatas)
|
||||
{
|
||||
var (perShaderData, perMaterialData, perInspectorData) = metaDatas.GetDatas();
|
||||
|
||||
bool hasChanged = false;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
var revertButtonRect = RevertableHelper.SplitRevertButtonRect(ref rect);
|
||||
|
||||
// Get internal TextField ControlID
|
||||
int controlId = GUIUtility.GetControlID(s_TextFieldHash, FocusType.Keyboard, rect) + 1;
|
||||
|
||||
// searching mode
|
||||
Rect modeRect = new Rect(rect);
|
||||
modeRect.width = 20f;
|
||||
if (Event.current.type == UnityEngine.EventType.MouseDown && modeRect.Contains(Event.current.mousePosition))
|
||||
{
|
||||
EditorUtility.DisplayCustomMenu(rect, _searchModeMenus, (int)perShaderData.searchMode,
|
||||
(data, options, selected) =>
|
||||
{
|
||||
perShaderData.searchMode = (SearchMode)selected;
|
||||
hasChanged = true;
|
||||
}, null);
|
||||
Event.current.Use();
|
||||
}
|
||||
|
||||
perShaderData.searchString = EditorGUI.TextField(rect, String.Empty, perShaderData.searchString, guiStyles_ToolbarSearchTextFieldPopup);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
hasChanged = true;
|
||||
|
||||
// revert button
|
||||
if (!string.IsNullOrEmpty(perShaderData.searchString)
|
||||
&& RevertableHelper.DrawRevertButton(revertButtonRect))
|
||||
{
|
||||
perShaderData.searchString = string.Empty;
|
||||
hasChanged = true;
|
||||
GUIUtility.keyboardControl = 0;
|
||||
}
|
||||
|
||||
// display search mode
|
||||
if (GUIUtility.keyboardControl != controlId
|
||||
&& string.IsNullOrEmpty(perShaderData.searchString)
|
||||
&& Event.current.type == UnityEngine.EventType.Repaint)
|
||||
{
|
||||
using (new EditorGUI.DisabledScope(true))
|
||||
{
|
||||
var disableTextRect = new Rect(rect.x, rect.y, rect.width,
|
||||
guiStyles_ToolbarSearchTextFieldPopup.fixedHeight > 0.0
|
||||
? guiStyles_ToolbarSearchTextFieldPopup.fixedHeight
|
||||
: rect.height);
|
||||
disableTextRect = guiStyles_ToolbarSearchTextFieldPopup.padding.Remove(disableTextRect);
|
||||
int fontSize = EditorStyles.label.fontSize;
|
||||
EditorStyles.label.fontSize = guiStyles_ToolbarSearchTextFieldPopup.fontSize;
|
||||
EditorStyles.label.Draw(disableTextRect, new GUIContent(perShaderData.searchMode.ToString()), false, false, false, false);
|
||||
EditorStyles.label.fontSize = fontSize;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasChanged) perShaderData.UpdateSearchFilter();
|
||||
|
||||
return hasChanged;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Context Menu
|
||||
|
||||
private static void EditPresetEvent(string mode, LwguiShaderPropertyPreset presetAsset, List<LwguiShaderPropertyPreset.Preset> targetPresets, MaterialProperty prop, LWGUIMetaDatas metaDatas)
|
||||
{
|
||||
if (!VersionControlHelper.Checkout(presetAsset))
|
||||
{
|
||||
Debug.LogError("LWGUI: Can not edit the preset: " + presetAsset);
|
||||
return;
|
||||
}
|
||||
foreach (var targetPreset in targetPresets)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case "Add":
|
||||
case "Update":
|
||||
targetPreset.AddOrUpdateIncludeExtraProperties(metaDatas, prop);
|
||||
break;
|
||||
case "Remove":
|
||||
targetPreset.RemoveIncludeExtraProperties(metaDatas, prop.name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
EditorUtility.SetDirty(presetAsset);
|
||||
MetaDataHelper.ForceUpdateMaterialMetadataCache(metaDatas.GetMaterial());
|
||||
}
|
||||
|
||||
public static void DoPropertyContextMenus(Rect rect, MaterialProperty prop, LWGUIMetaDatas metaDatas)
|
||||
{
|
||||
if (Event.current.type != EventType.ContextClick || !rect.Contains(Event.current.mousePosition)) return;
|
||||
|
||||
Event.current.Use();
|
||||
|
||||
var (perShaderData, perMaterialData, perInspectorData) = metaDatas.GetDatas();
|
||||
var (propStaticData, propDynamicData) = metaDatas.GetPropDatas(prop);
|
||||
var menu = new GenericMenu();
|
||||
|
||||
// 2022+ Material Varant Menus
|
||||
#if UNITY_2022_1_OR_NEWER
|
||||
ReflectionHelper.HandleApplyRevert(menu, prop);
|
||||
#endif
|
||||
|
||||
// Copy
|
||||
menu.AddItem(new GUIContent("Copy"), false, () =>
|
||||
{
|
||||
_copiedMaterial = UnityEngine.Object.Instantiate(metaDatas.GetMaterial());
|
||||
_copiedProps.Clear();
|
||||
_copiedProps.Add(prop.name);
|
||||
foreach (var extraPropName in propStaticData.extraPropNames)
|
||||
{
|
||||
_copiedProps.Add(extraPropName);
|
||||
}
|
||||
|
||||
// Copy Children
|
||||
foreach (var childPropStaticData in propStaticData.children)
|
||||
{
|
||||
_copiedProps.Add(childPropStaticData.name);
|
||||
foreach (var extraPropName in childPropStaticData.extraPropNames)
|
||||
{
|
||||
_copiedProps.Add(extraPropName);
|
||||
}
|
||||
|
||||
foreach (var childChildPropStaticData in childPropStaticData.children)
|
||||
{
|
||||
_copiedProps.Add(childChildPropStaticData.name);
|
||||
foreach (var extraPropName in childChildPropStaticData.extraPropNames)
|
||||
{
|
||||
_copiedProps.Add(extraPropName);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Paste
|
||||
GenericMenu.MenuFunction pasteAction = () =>
|
||||
{
|
||||
if (!VersionControlHelper.Checkout(prop.targets))
|
||||
{
|
||||
Debug.LogError("LWGUI: One or more materials unable to write!");
|
||||
return;
|
||||
}
|
||||
|
||||
Undo.RecordObjects(prop.targets, "LWGUI: Paste Material Properties");
|
||||
|
||||
foreach (Material material in prop.targets)
|
||||
{
|
||||
var index = 0;
|
||||
|
||||
PastePropertyValueToMaterial(material, _copiedProps[index++], prop.name);
|
||||
foreach (var extraPropName in propStaticData.extraPropNames)
|
||||
{
|
||||
if (index == _copiedProps.Count) break;
|
||||
PastePropertyValueToMaterial(material, _copiedProps[index++], extraPropName);
|
||||
}
|
||||
|
||||
// Paste Children
|
||||
foreach (var childPropStaticData in propStaticData.children)
|
||||
{
|
||||
if (index == _copiedProps.Count) break;
|
||||
PastePropertyValueToMaterial(material, _copiedProps[index++], childPropStaticData.name);
|
||||
foreach (var extraPropName in childPropStaticData.extraPropNames)
|
||||
{
|
||||
if (index == _copiedProps.Count) break;
|
||||
PastePropertyValueToMaterial(material, _copiedProps[index++], extraPropName);
|
||||
}
|
||||
|
||||
foreach (var childChildPropStaticData in childPropStaticData.children)
|
||||
{
|
||||
if (index == _copiedProps.Count) break;
|
||||
PastePropertyValueToMaterial(material, _copiedProps[index++], childChildPropStaticData.name);
|
||||
foreach (var extraPropName in childChildPropStaticData.extraPropNames)
|
||||
{
|
||||
if (index == _copiedProps.Count) break;
|
||||
PastePropertyValueToMaterial(material, _copiedProps[index++], extraPropName);
|
||||
}
|
||||
}
|
||||
}
|
||||
MetaDataHelper.ForceUpdateMaterialMetadataCache(material);
|
||||
}
|
||||
};
|
||||
|
||||
if (_copiedMaterial != null && _copiedProps.Count > 0 && GUI.enabled)
|
||||
menu.AddItem(new GUIContent("Paste"), false, pasteAction);
|
||||
else
|
||||
menu.AddDisabledItem(new GUIContent("Paste"));
|
||||
|
||||
menu.AddSeparator("");
|
||||
|
||||
// Copy Display Name
|
||||
menu.AddItem(new GUIContent("Copy Display Name"), false, () =>
|
||||
{
|
||||
EditorGUIUtility.systemCopyBuffer = propStaticData.displayName;
|
||||
});
|
||||
|
||||
// Copy Property Names
|
||||
menu.AddItem(new GUIContent("Copy Property Names"), false, () =>
|
||||
{
|
||||
EditorGUIUtility.systemCopyBuffer = prop.name;
|
||||
foreach (var extraPropName in propStaticData.extraPropNames)
|
||||
{
|
||||
EditorGUIUtility.systemCopyBuffer += ", " + extraPropName;
|
||||
}
|
||||
});
|
||||
|
||||
// menus.AddSeparator("");
|
||||
//
|
||||
// // Add to Favorites
|
||||
// menus.AddItem(new GUIContent("Add to Favorites"), false, () =>
|
||||
// {
|
||||
// });
|
||||
//
|
||||
// // Remove from Favorites
|
||||
// menus.AddItem(new GUIContent("Remove from Favorites"), false, () =>
|
||||
// {
|
||||
// });
|
||||
|
||||
// Preset
|
||||
if (GUI.enabled)
|
||||
{
|
||||
menu.AddSeparator("");
|
||||
foreach (var activePresetData in perMaterialData.activePresetDatas)
|
||||
{
|
||||
// Cull self
|
||||
if (activePresetData.property == prop) continue;
|
||||
|
||||
var activePreset = activePresetData.preset;
|
||||
var (presetPropStaticData, presetPropDynamicData) = metaDatas.GetPropDatas(activePresetData.property);
|
||||
var presetAsset = presetPropStaticData.propertyPresetAsset;
|
||||
var presetPropDisplayName = presetPropStaticData.displayName;
|
||||
|
||||
// Cull invisible presets
|
||||
if (!presetPropDynamicData.isShowing) continue;
|
||||
|
||||
if (activePreset.GetPropertyValue(prop.name) != null)
|
||||
{
|
||||
menu.AddItem(new GUIContent("Update to Preset/" + presetPropDisplayName + "/" + "All"), false, () => EditPresetEvent("Update", presetAsset, presetAsset.GetPresets(), prop, metaDatas));
|
||||
menu.AddItem(new GUIContent("Update to Preset/" + presetPropDisplayName + "/" + activePreset.presetName), false, () => EditPresetEvent("Update", presetAsset, new List<LwguiShaderPropertyPreset.Preset>(){activePreset}, prop, metaDatas));
|
||||
menu.AddItem(new GUIContent("Remove from Preset/" + presetPropDisplayName + "/" + "All"), false, () => EditPresetEvent("Remove", presetAsset, presetAsset.GetPresets(), prop, metaDatas));
|
||||
menu.AddItem(new GUIContent("Remove from Preset/" + presetPropDisplayName + "/" + activePreset.presetName), false, () => EditPresetEvent("Remove", presetAsset, new List<LwguiShaderPropertyPreset.Preset>(){activePreset}, prop, metaDatas));
|
||||
}
|
||||
else
|
||||
{
|
||||
menu.AddItem(new GUIContent("Add to Preset/" + presetPropDisplayName + "/" + "All"), false, () => EditPresetEvent("Add", presetAsset, presetAsset.GetPresets(), prop, metaDatas));
|
||||
menu.AddItem(new GUIContent("Add to Preset/" + presetPropDisplayName + "/" + activePreset.presetName), false, () => EditPresetEvent("Add", presetAsset, new List<LwguiShaderPropertyPreset.Preset>(){activePreset}, prop, metaDatas));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Custom
|
||||
if (propStaticData.baseDrawers != null)
|
||||
{
|
||||
foreach (var baseDrawer in propStaticData.baseDrawers)
|
||||
{
|
||||
baseDrawer.GetCustomContextMenus(menu, rect, prop, metaDatas);
|
||||
}
|
||||
}
|
||||
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Importer
|
||||
|
||||
// https://docs.unity3d.com/ScriptReference/TextureImporter.GetPlatformTextureSettings.html
|
||||
public static string[] platformNamesForTextureSettings => new[] { "DefaultTexturePlatform", "Standalone", "Web", "iPhone", "Android", "WebGL", "Windows Store Apps", "PS4", "XboxOne", "Nintendo Switch", "tvOS" };
|
||||
public static string[] platformNamesForTextureSettings =>
|
||||
new[] { "DefaultTexturePlatform", "Standalone", "Web", "iPhone", "Android", "WebGL", "Windows Store Apps", "PS4", "XboxOne", "Nintendo Switch", "tvOS" };
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@ -0,0 +1,215 @@
|
||||
// Copyright (c) Jason Ma
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using LWGUI.PerformanceMonitor;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
namespace LWGUI
|
||||
{
|
||||
public static class IOHelper
|
||||
{
|
||||
#region Paths
|
||||
|
||||
private static string _cachedProjectPath;
|
||||
|
||||
// D:/Unity/ProjectName/
|
||||
public static string ProjectPath
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(_cachedProjectPath))
|
||||
_cachedProjectPath = Application.dataPath[..^6];
|
||||
return _cachedProjectPath;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetAbsPath(string unityProjectRelativePath) => Path.Combine(ProjectPath, unityProjectRelativePath);
|
||||
|
||||
public static string GetRelativePath(string absPath) => Path.GetFullPath(absPath).Replace(Path.GetFullPath(ProjectPath), string.Empty);
|
||||
|
||||
private static string _cachedCompiledShaderCachePath;
|
||||
|
||||
public static string CompiledShaderCacheRootPath
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(_cachedCompiledShaderCachePath))
|
||||
_cachedCompiledShaderCachePath = Path.Combine(ProjectPath, "Library", "LWGUI", "ShaderPerfCache");
|
||||
return _cachedCompiledShaderCachePath;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetValidFileName(string text)
|
||||
{
|
||||
StringBuilder str = new StringBuilder();
|
||||
var invalidFileNameChars = Path.GetInvalidFileNameChars();
|
||||
foreach (var c in text)
|
||||
{
|
||||
if (!invalidFileNameChars.Contains(c))
|
||||
{
|
||||
str.Append(c);
|
||||
}
|
||||
}
|
||||
|
||||
return str.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region File
|
||||
|
||||
public static bool ExistAndNotEmpty(string filePath) => File.Exists(filePath) && new FileInfo(filePath).Length > 1;
|
||||
|
||||
public static void WriteBinaryFile(string filePath, byte[] bytes)
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(filePath) ?? string.Empty);
|
||||
File.WriteAllBytes(filePath, bytes ?? Array.Empty<byte>());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void WriteTextFile(string filePath, string text)
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(filePath) ?? string.Empty);
|
||||
File.WriteAllText(filePath, text ?? string.Empty, Encoding.UTF8);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ReadTextFile(string filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
return File.ReadAllText(filePath, Encoding.UTF8);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Process
|
||||
|
||||
public static bool RunProcess(string file, string args,
|
||||
out string output)
|
||||
{
|
||||
output = string.Empty;
|
||||
|
||||
var p = new Process
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = file,
|
||||
Arguments = args,
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
CreateNoWindow = true,
|
||||
StandardOutputEncoding = Encoding.UTF8,
|
||||
StandardErrorEncoding = Encoding.UTF8
|
||||
}
|
||||
};
|
||||
|
||||
var stdout = new StringBuilder();
|
||||
var stderr = new StringBuilder();
|
||||
|
||||
p.OutputDataReceived += (_, e) => stdout.AppendLine(e.Data);
|
||||
p.ErrorDataReceived += (_, e) => stderr.AppendLine(e.Data);
|
||||
|
||||
p.Start();
|
||||
p.BeginOutputReadLine();
|
||||
p.BeginErrorReadLine();
|
||||
p.WaitForExit();
|
||||
|
||||
output = stdout.ToString();
|
||||
|
||||
if (p.ExitCode != 0)
|
||||
{
|
||||
output = stderr.ToString() + output;
|
||||
Debug.LogError($"LWGUI: Process Exit Code {p.ExitCode}: {output}" +
|
||||
$"File: {file}\n" +
|
||||
$"Args: {args}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(output))
|
||||
{
|
||||
output = stderr.ToString();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool RunCMD(string args,
|
||||
out string output)
|
||||
{
|
||||
return RunProcess("cmd.exe", $"/C {args}",
|
||||
out output);
|
||||
}
|
||||
|
||||
public static void OpenFile(string filePath)
|
||||
{
|
||||
Process.Start(filePath);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Performance Monitor
|
||||
|
||||
public static string GetCompiledShaderCacheRootDirectory(Shader shader)
|
||||
{
|
||||
var shaderPath = AssetDatabase.Contains(shader) ? AssetDatabase.GetAssetPath(shader) : null;
|
||||
var shaderCachePath = shaderPath != null
|
||||
// Assets/Shaders/Lit.shader => Shaders/Lit
|
||||
? Path.Combine(Path.GetDirectoryName(shaderPath[7..]) ?? string.Empty, Path.GetFileNameWithoutExtension(shaderPath))
|
||||
: GetValidFileName(shader.name.Replace('/', '_').Replace('\\', '_'));
|
||||
|
||||
return Path.Combine(CompiledShaderCacheRootPath, shaderCachePath);
|
||||
}
|
||||
|
||||
public static string GetCompiledShaderVariantCacheDirectory(Shader shader, ShaderPerfData shaderPerfData)
|
||||
{
|
||||
return Path.Combine(
|
||||
GetCompiledShaderCacheRootDirectory(shader),
|
||||
shaderPerfData.subshaderIndex.ToString(),
|
||||
shaderPerfData.passName,
|
||||
shaderPerfData.hash);
|
||||
}
|
||||
|
||||
public static void ClearShaderPerfCache(Shader shader)
|
||||
{
|
||||
try
|
||||
{
|
||||
var shaderDir = GetCompiledShaderCacheRootDirectory(shader);
|
||||
if (Directory.Exists(shaderDir))
|
||||
Directory.Delete(shaderDir, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning($"LWGUI: Cleaning the Shader({shader.name}) cache failed: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d97df5d9f08d48ac9dedc5b421b936dc
|
||||
timeCreated: 1760615660
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright (c) Jason Ma
|
||||
// Copyright (c) Jason Ma
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@ -14,7 +14,6 @@ namespace LWGUI
|
||||
public PerMaterialData perMaterialData;
|
||||
public PerInspectorData perInspectorData;
|
||||
|
||||
|
||||
#region Get Prop Data
|
||||
|
||||
public PropertyStaticData GetPropStaticData(string propName) => perShaderData?.GetPropStaticData(propName);
|
||||
@ -27,7 +26,7 @@ namespace LWGUI
|
||||
|
||||
public MaterialProperty GetProperty(string propName) => GetPropDynamicData(propName)?.property;
|
||||
|
||||
public MaterialProperty GetDefaultProperty(string propName) => GetPropDynamicData(propName)?.defualtProperty;
|
||||
public MaterialProperty GetDefaultProperty(string propName) => GetPropDynamicData(propName)?.defaultProperty;
|
||||
|
||||
#endregion
|
||||
|
||||
@ -47,6 +46,8 @@ namespace LWGUI
|
||||
public Material GetMaterial() => perMaterialData.material;
|
||||
|
||||
public Shader GetShader() => perShaderData.shader;
|
||||
|
||||
public string GetShaderUID() => perShaderData.shaderUID;
|
||||
|
||||
public MaterialEditor GetMaterialEditor() => perInspectorData.materialEditor;
|
||||
}
|
||||
|
||||
@ -53,14 +53,18 @@ namespace LWGUI
|
||||
|
||||
if (!_loadedPresets.ContainsKey(presetFileName) || !_loadedPresets[presetFileName])
|
||||
{
|
||||
Debug.LogError("LWGUI: Invalid ShaderPropertyPreset path: ‘" + presetFileName + "’ !");
|
||||
if (!BuildPipeline.isBuildingPlayer)
|
||||
Debug.LogError("LWGUI: Invalid ShaderPropertyPreset path: ‘" + presetFileName + "’ !");
|
||||
return null;
|
||||
}
|
||||
|
||||
return _loadedPresets[presetFileName];
|
||||
}
|
||||
|
||||
// For Developers: Call this after a material has modified in code
|
||||
// For Developers: Call this function after creating a material,
|
||||
// This applies all active presets and may modify some other properties.
|
||||
// Usually called after the material is created, otherwise the material default value will not contain the results of Preset Drawers.
|
||||
// If you only want to apply Keywords without modifying other properties, call UnityEditorExtension.ApplyMaterialPropertyAndDecoratorDrawers()
|
||||
public static void ApplyPresetsInMaterial(Material material)
|
||||
{
|
||||
var props = MaterialEditor.GetMaterialProperties(new UnityEngine.Object[] { material });
|
||||
@ -69,11 +73,10 @@ namespace LWGUI
|
||||
var drawer = ReflectionHelper.GetPropertyDrawer(material.shader, prop, out _);
|
||||
|
||||
// Apply active preset
|
||||
if (drawer != null && drawer is IPresetDrawer)
|
||||
if (drawer is IPresetDrawer presetDrawer)
|
||||
{
|
||||
var activePreset = (drawer as IPresetDrawer).GetActivePreset(prop, PresetHelper.GetPresetAsset((drawer as PresetDrawer).presetFileName));
|
||||
if (activePreset != null)
|
||||
activePreset.ApplyToDefaultMaterial(material);
|
||||
var activePreset = presetDrawer.GetActivePreset(prop, GetPresetAsset(presetDrawer.GetPresetFileName()));
|
||||
activePreset?.ApplyToDefaultMaterial(material);
|
||||
}
|
||||
}
|
||||
UnityEditorExtension.ApplyMaterialPropertyAndDecoratorDrawers(material);
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
// Copyright (c) Jason Ma
|
||||
using System;
|
||||
// Copyright (c) Jason Ma
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using LWGUI.LwguiGradientEditor;
|
||||
using LWGUI.Runtime.LwguiGradient;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace LWGUI
|
||||
{
|
||||
@ -14,10 +12,13 @@ namespace LWGUI
|
||||
{
|
||||
#region RampEditor
|
||||
|
||||
private static readonly GUIContent _iconAdd = new GUIContent(EditorGUIUtility.IconContent("d_Toolbar Plus").image, "Add"),
|
||||
_iconEdit = new GUIContent(EditorGUIUtility.IconContent("editicon.sml").image, "Edit"),
|
||||
_iconDiscard = new GUIContent(EditorGUIUtility.IconContent("d_TreeEditor.Refresh").image, "Discard"),
|
||||
_iconSave = new GUIContent(EditorGUIUtility.IconContent("SaveActive").image, "Save");
|
||||
private const string _iconCloneGUID = "9cdef444d18d2ce4abb6bbc4fed4d109";
|
||||
|
||||
private static readonly GUIContent _iconAdd = new (EditorGUIUtility.IconContent("d_Toolbar Plus").image, "Add"),
|
||||
_iconClone = new (EditorGUIUtility.IconContent("AnimatorController Icon").image, "Clone"),
|
||||
_iconEdit = new (EditorGUIUtility.IconContent("editicon.sml").image, "Edit"),
|
||||
_iconDiscard = new (EditorGUIUtility.IconContent("d_TreeEditor.Refresh").image, "Discard"),
|
||||
_iconSave = new (EditorGUIUtility.IconContent("SaveActive").image, "Save");
|
||||
|
||||
public static void RampEditor(
|
||||
Rect buttonRect,
|
||||
@ -29,6 +30,7 @@ namespace LWGUI
|
||||
out bool hasChange,
|
||||
out bool doEditWhenNoGradient,
|
||||
out bool doRegisterUndo,
|
||||
out bool doClone,
|
||||
out bool doCreate,
|
||||
out bool doSave,
|
||||
out bool doDiscard,
|
||||
@ -38,11 +40,12 @@ namespace LWGUI
|
||||
var hasNoGradient = gradient == null;
|
||||
var _doEditWhenNoGradient = false;
|
||||
var doOpenWindow = false;
|
||||
var singleButtonWidth = buttonRect.width * 0.25f;
|
||||
var singleButtonWidth = buttonRect.width * 0.2f;
|
||||
var editRect = new Rect(buttonRect.x + singleButtonWidth * 0, buttonRect.y, singleButtonWidth, buttonRect.height);
|
||||
var saveRect = new Rect(buttonRect.x + singleButtonWidth * 1, buttonRect.y, singleButtonWidth, buttonRect.height);
|
||||
var addRect = new Rect(buttonRect.x + singleButtonWidth * 2, buttonRect.y, singleButtonWidth, buttonRect.height);
|
||||
var discardRect = new Rect(buttonRect.x + singleButtonWidth * 3, buttonRect.y, singleButtonWidth, buttonRect.height);
|
||||
var cloneRect = new Rect(buttonRect.x + singleButtonWidth * 2, buttonRect.y, singleButtonWidth, buttonRect.height);
|
||||
var addRect = new Rect(buttonRect.x + singleButtonWidth * 3, buttonRect.y, singleButtonWidth, buttonRect.height);
|
||||
var discardRect = new Rect(buttonRect.x + singleButtonWidth * 4, buttonRect.y, singleButtonWidth, buttonRect.height);
|
||||
|
||||
// Edit button event
|
||||
hasChange = false;
|
||||
@ -76,6 +79,8 @@ namespace LWGUI
|
||||
}
|
||||
doEditWhenNoGradient = _doEditWhenNoGradient;
|
||||
|
||||
// Clone button
|
||||
doClone = GUI.Button(cloneRect, _iconClone);
|
||||
|
||||
// Create button
|
||||
doCreate = GUI.Button(addRect, _iconAdd);
|
||||
@ -143,9 +148,8 @@ namespace LWGUI
|
||||
// Save texture to disk
|
||||
if (doSaveToDisk)
|
||||
{
|
||||
var systemPath = Helper.ProjectPath + path;
|
||||
VersionControlHelper.Checkout(path);
|
||||
File.WriteAllBytes(systemPath, texture2D.EncodeToPNG());
|
||||
File.WriteAllBytes(IOHelper.GetAbsPath(path), texture2D.EncodeToPNG());
|
||||
assetImporter.SaveAndReimport();
|
||||
}
|
||||
}
|
||||
@ -190,15 +194,14 @@ namespace LWGUI
|
||||
return subJSONs[0] != subJSONs[1];
|
||||
}
|
||||
|
||||
public static bool CreateAndSaveNewGradientTexture(int width, int height, string unityPath, bool isLinear)
|
||||
public static bool CreateAndSaveNewGradientTexture(int width, int height, string unityPath, bool isLinear, LwguiGradient sourceGradient = null)
|
||||
{
|
||||
var gradient = new LwguiGradient();
|
||||
var gradient = sourceGradient != null ? new LwguiGradient(sourceGradient) : new LwguiGradient();
|
||||
|
||||
var ramp = gradient.GetPreviewRampTexture(width, height, ColorSpace.Linear);
|
||||
var png = ramp.EncodeToPNG();
|
||||
|
||||
var systemPath = Helper.ProjectPath + unityPath;
|
||||
File.WriteAllBytes(systemPath, png);
|
||||
File.WriteAllBytes(IOHelper.GetAbsPath(unityPath), png);
|
||||
|
||||
AssetDatabase.ImportAsset(unityPath);
|
||||
SetRampTextureImporter(unityPath, true, isLinear, EncodeGradientToJSON(gradient, gradient));
|
||||
@ -262,33 +265,40 @@ namespace LWGUI
|
||||
}
|
||||
}
|
||||
|
||||
public static void RampIndexSelectorOverride(Rect rect, MaterialProperty prop, LwguiRampAtlas rampAtlas, RampSelectorWindow.SwitchRampMapCallback switchRampMapEvent)
|
||||
{
|
||||
if (!rampAtlas)
|
||||
return;
|
||||
|
||||
var e = Event.current;
|
||||
if (e.type == UnityEngine.EventType.MouseDown && rect.Contains(e.mousePosition))
|
||||
{
|
||||
e.Use();
|
||||
RampSelectorWindow.ShowWindow(prop, rampAtlas.GetTexture2Ds(LwguiGradient.ChannelMask.RGB), switchRampMapEvent);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class RampSelectorWindow : EditorWindow
|
||||
{
|
||||
public delegate void SwitchRampMapCallback(MaterialProperty prop, Texture2D newRampMap, int index);
|
||||
|
||||
public delegate void SwitchRampCallback(MaterialProperty prop, int rampIndex);
|
||||
|
||||
private LwguiRampAtlas _rampAtlas;
|
||||
private Texture2D[] _rampMaps;
|
||||
private Vector2 _scrollPosition;
|
||||
private MaterialProperty _prop;
|
||||
private SwitchRampCallback _switchRampEvent;
|
||||
private SwitchRampMapCallback _switchRampMapEvent;
|
||||
|
||||
private const float RowHeight = 18f;
|
||||
private const float RowSpacing = 2f;
|
||||
|
||||
public static void ShowWindow(MaterialProperty prop, LwguiRampAtlas rampAtlas, SwitchRampCallback switchRampEvent)
|
||||
{
|
||||
LwguiGradientWindow.CloseWindow();
|
||||
var window = CreateInstance<RampSelectorWindow>();
|
||||
window.titleContent = new GUIContent("Ramp Selector (Atlas)");
|
||||
window.minSize = new Vector2(400, 500);
|
||||
window._rampAtlas = rampAtlas;
|
||||
window._prop = prop;
|
||||
window._switchRampEvent = switchRampEvent;
|
||||
window.ShowAuxWindow();
|
||||
}
|
||||
|
||||
public static void ShowWindow(MaterialProperty prop, Texture2D[] rampMaps, SwitchRampMapCallback switchRampMapEvent)
|
||||
{
|
||||
RampSelectorWindow window = ScriptableObject.CreateInstance<RampSelectorWindow>();
|
||||
LwguiGradientWindow.CloseWindow();
|
||||
var window = CreateInstance<RampSelectorWindow>();
|
||||
window.titleContent = new GUIContent("Ramp Selector");
|
||||
window.minSize = new Vector2(400, 500);
|
||||
window._rampMaps = rampMaps;
|
||||
@ -298,6 +308,62 @@ namespace LWGUI
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
if (_rampAtlas != null)
|
||||
DrawRampAtlasSelector();
|
||||
else if (_rampMaps != null)
|
||||
DrawRampMapSelector();
|
||||
else
|
||||
EditorGUILayout.HelpBox("No Ramp data available", MessageType.Error);
|
||||
}
|
||||
|
||||
private void DrawRampAtlasSelector()
|
||||
{
|
||||
EditorGUILayout.BeginVertical();
|
||||
_scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition);
|
||||
|
||||
for (int i = 0; i < _rampAtlas.RampCount; i++)
|
||||
{
|
||||
var ramp = _rampAtlas.GetRamp(i);
|
||||
if (ramp == null) continue;
|
||||
|
||||
var previewTextures = ramp.GetPreviewTexturesForRampSelector(_rampAtlas.rampAtlasWidth);
|
||||
var textureCount = previewTextures?.Length ?? 0;
|
||||
var totalHeight = Mathf.Max(1, textureCount) * RowHeight + Mathf.Max(0, textureCount - 1) * RowSpacing;
|
||||
|
||||
var rect = EditorGUILayout.GetControlRect(GUILayout.Height(totalHeight));
|
||||
var guiContent = new GUIContent($"{i}. {ramp.Name}");
|
||||
var buttonWidth = Mathf.Min(300f, Mathf.Max(GUI.skin.button.CalcSize(guiContent).x, rect.width * 0.35f));
|
||||
var buttonRect = new Rect(rect.x + rect.width - buttonWidth, rect.y, buttonWidth, totalHeight);
|
||||
var previewWidth = rect.width - buttonWidth - 3.0f;
|
||||
|
||||
// Draw preview textures vertically
|
||||
if (previewTextures != null)
|
||||
{
|
||||
for (int j = 0; j < previewTextures.Length; j++)
|
||||
{
|
||||
if (previewTextures[j] == null) continue;
|
||||
var previewRect = new Rect(rect.x, rect.y + j * (RowHeight + RowSpacing), previewWidth, RowHeight);
|
||||
EditorGUI.DrawPreviewTexture(previewRect, previewTextures[j]);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw button (stretches to cover all preview rows)
|
||||
if (GUI.Button(buttonRect, guiContent, GUIStyles.rampSelectButton) && _switchRampEvent != null)
|
||||
{
|
||||
_switchRampEvent(_prop, i);
|
||||
LwguiGradientWindow.CloseWindow();
|
||||
Close();
|
||||
}
|
||||
|
||||
GUILayout.Space(RowSpacing);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
private void DrawRampMapSelector()
|
||||
{
|
||||
EditorGUILayout.BeginVertical();
|
||||
_scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition);
|
||||
@ -305,26 +371,26 @@ namespace LWGUI
|
||||
for (int i = 0; i < _rampMaps.Length; i++)
|
||||
{
|
||||
var rampMap = _rampMaps[i];
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (rampMap != null)
|
||||
if (rampMap == null) continue;
|
||||
|
||||
var rect = EditorGUILayout.GetControlRect(GUILayout.Height(RowHeight));
|
||||
var guiContent = new GUIContent($"{i}. {rampMap.name}");
|
||||
var buttonWidth = Mathf.Min(300f, Mathf.Max(GUI.skin.button.CalcSize(guiContent).x, rect.width * 0.35f));
|
||||
var buttonRect = new Rect(rect.x + rect.width - buttonWidth, rect.y, buttonWidth, RowHeight);
|
||||
var previewRect = new Rect(rect.x, rect.y, rect.width - buttonWidth - 3.0f, RowHeight);
|
||||
|
||||
EditorGUI.DrawPreviewTexture(previewRect, rampMap);
|
||||
|
||||
if (GUI.Button(buttonRect, guiContent, GUIStyles.rampSelectButton) && _switchRampMapEvent != null)
|
||||
{
|
||||
var guiContent = new GUIContent($"{ i }. { rampMap.name }");
|
||||
var rect = EditorGUILayout.GetControlRect();
|
||||
var buttonWidth = Mathf.Min(300f, Mathf.Max(GUI.skin.button.CalcSize(guiContent).x, rect.width * 0.35f));
|
||||
var buttonRect = new Rect(rect.x + rect.width - buttonWidth, rect.y, buttonWidth, rect.height);
|
||||
var previewRect = new Rect(rect.x, rect.y, rect.width - buttonWidth - 3.0f, rect.height);
|
||||
|
||||
if (GUI.Button(buttonRect, guiContent, Helper.guiStyle_RampSelectButton) && _switchRampMapEvent != null)
|
||||
{
|
||||
_switchRampMapEvent(_prop, rampMap, i);
|
||||
LwguiGradientWindow.CloseWindow();
|
||||
Close();
|
||||
}
|
||||
EditorGUI.DrawPreviewTexture(previewRect, rampMap);
|
||||
_switchRampMapEvent(_prop, rampMap, i);
|
||||
LwguiGradientWindow.CloseWindow();
|
||||
Close();
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
GUILayout.Space(RowSpacing);
|
||||
}
|
||||
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright (c) Jason Ma
|
||||
// Copyright (c) Jason Ma
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
@ -136,11 +136,24 @@ namespace LWGUI
|
||||
|
||||
if (isHeader)
|
||||
{
|
||||
foreach (var childStaticData in propStaticData.children)
|
||||
RevertGroupChildren(propStaticData, metaDatas);
|
||||
|
||||
// Reverting a group that contains a preset may change which preset is active,
|
||||
// making the first-pass defaults stale. Re-init with fresh MaterialProperty
|
||||
// objects to recalculate defaults, then re-revert to get correct final values.
|
||||
// (Fresh props are needed because MaterialProperty.floatValue getter may
|
||||
// return stale cached values after writes within the same GUI event.)
|
||||
if (GroupContainsPreset(propStaticData))
|
||||
{
|
||||
DoRevertProperty(metaDatas.GetProperty(childStaticData.name), metaDatas);
|
||||
foreach (var childChildStaticData in childStaticData.children)
|
||||
DoRevertProperty(metaDatas.GetProperty(childChildStaticData.name), metaDatas);
|
||||
var perMaterialData = metaDatas.perMaterialData;
|
||||
var freshProps = MaterialEditor.GetMaterialProperties(
|
||||
new UnityEngine.Object[] { perMaterialData.material });
|
||||
perMaterialData.InvalidateDefaultMaterialCache();
|
||||
perMaterialData.Init(metaDatas.GetShader(), perMaterialData.material,
|
||||
metaDatas.GetMaterialEditor(), freshProps, metaDatas.perShaderData);
|
||||
|
||||
DoRevertProperty(prop, metaDatas);
|
||||
RevertGroupChildren(propStaticData, metaDatas);
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,16 +162,43 @@ namespace LWGUI
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void RevertGroupChildren(PropertyStaticData propStaticData, LWGUIMetaDatas metaDatas)
|
||||
{
|
||||
foreach (var childStaticData in propStaticData.children)
|
||||
{
|
||||
DoRevertProperty(metaDatas.GetProperty(childStaticData.name), metaDatas);
|
||||
foreach (var childChildStaticData in childStaticData.children)
|
||||
DoRevertProperty(metaDatas.GetProperty(childChildStaticData.name), metaDatas);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool GroupContainsPreset(PropertyStaticData propStaticData)
|
||||
{
|
||||
if (propStaticData.presetDrawer != null)
|
||||
return true;
|
||||
foreach (var child in propStaticData.children)
|
||||
{
|
||||
if (child.presetDrawer != null)
|
||||
return true;
|
||||
foreach (var grandchild in child.children)
|
||||
{
|
||||
if (grandchild.presetDrawer != null)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void DoRevertProperty(MaterialProperty prop, LWGUIMetaDatas metaDatas)
|
||||
{
|
||||
var propDynamicData = metaDatas.GetPropDynamicData(prop.name);
|
||||
propDynamicData.hasRevertChanged = true;
|
||||
SetPropertyToDefault(propDynamicData.defualtProperty, prop);
|
||||
SetPropertyToDefault(propDynamicData.defaultProperty, prop);
|
||||
foreach (var extraPropName in metaDatas.GetPropStaticData(prop.name).extraPropNames)
|
||||
{
|
||||
var extraPropDynamicData = metaDatas.GetPropDynamicData(extraPropName);
|
||||
extraPropDynamicData.hasRevertChanged = true;
|
||||
SetPropertyToDefault(extraPropDynamicData.defualtProperty, extraPropDynamicData.property);
|
||||
SetPropertyToDefault(extraPropDynamicData.defaultProperty, extraPropDynamicData.property);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,578 @@
|
||||
// Copyright (c) Jason Ma
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using LWGUI.PerformanceMonitor;
|
||||
|
||||
namespace LWGUI
|
||||
{
|
||||
public static class ToolbarHelper
|
||||
{
|
||||
#region Toolbar Buttons
|
||||
|
||||
internal enum CopyMaterialValueMask
|
||||
{
|
||||
Float = 1 << 0,
|
||||
Vector = 1 << 1,
|
||||
Texture = 1 << 2,
|
||||
Keyword = 1 << 3,
|
||||
RenderQueue = 1 << 4,
|
||||
Number = Float | Vector,
|
||||
All = (1 << 5) - 1,
|
||||
}
|
||||
|
||||
public const uint CopyMaterialValueMaskAll = (uint)CopyMaterialValueMask.All;
|
||||
|
||||
private static GUIContent[] _pasteMaterialMenus = new[]
|
||||
{
|
||||
new GUIContent("Paste Number Values"),
|
||||
new GUIContent("Paste Texture Values"),
|
||||
new GUIContent("Paste Keywords"),
|
||||
new GUIContent("Paste RenderQueue"),
|
||||
};
|
||||
|
||||
private static uint[] _pasteMaterialMenuValueMasks = new[]
|
||||
{
|
||||
(uint)CopyMaterialValueMask.Number,
|
||||
(uint)CopyMaterialValueMask.Texture,
|
||||
(uint)CopyMaterialValueMask.Keyword,
|
||||
(uint)CopyMaterialValueMask.RenderQueue,
|
||||
};
|
||||
|
||||
|
||||
private const string _iconCopyGUID = "9cdef444d18d2ce4abb6bbc4fed4d109";
|
||||
private const string _iconPasteGUID = "8e7a78d02e4c3574998524a0842a8ccb";
|
||||
private const string _iconSelectGUID = "6f44e40b24300974eb607293e4224ecc";
|
||||
private const string _iconCheckoutGUID = "72488141525eaa8499e65e52755cb6d0";
|
||||
private const string _iconExpandGUID = "2382450e7f4ddb94c9180d6634c41378";
|
||||
private const string _iconCollapseGUID = "929b6e5dfacc42b429d715a3e1ca2b57";
|
||||
private const string _iconStatsGUID = "88909414120107547a673b8fcddc5236";
|
||||
private const string _iconVisibilityGUID = "9576e23a695b35d49a9fc55c9a948b4f";
|
||||
|
||||
private const string _iconCopyTooltip = "Copy Material Properties";
|
||||
private const string _iconPasteTooltip = "Paste Material Properties\n\nRight-click to paste values by type.";
|
||||
private const string _iconSelectTooltip = "Select the Material Asset\n\nUsed to jump from a Runtime Material Instance to a Material Asset.";
|
||||
private const string _iconCheckoutTooltip = "Checkout selected Material Assets";
|
||||
private const string _iconExpandTooltip = "Expand All Groups";
|
||||
private const string _iconCollapseTooltip = "Collapse All Groups";
|
||||
private const string _iconStatsTooltip = "Display Shader Performance Stats";
|
||||
private const string _iconVisibilityTooltip = "Display Mode";
|
||||
|
||||
private static GUIContent _guiContentCopyCache;
|
||||
private static GUIContent _guiContentPasteCache;
|
||||
private static GUIContent _guiContentSelectCache;
|
||||
private static GUIContent _guiContentCheckoutCache;
|
||||
private static GUIContent _guiContentExpandCache;
|
||||
private static GUIContent _guiContentCollapseCache;
|
||||
private static GUIContent _guiContentStatsCache;
|
||||
private static GUIContent _guiContentVisibilityCache;
|
||||
|
||||
private static GUIContent _guiContentCopy => _guiContentCopyCache = _guiContentCopyCache ?? new GUIContent("", AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath(_iconCopyGUID)), _iconCopyTooltip);
|
||||
private static GUIContent _guiContentPaste => _guiContentPasteCache = _guiContentPasteCache ?? new GUIContent("", AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath(_iconPasteGUID)), _iconPasteTooltip);
|
||||
private static GUIContent _guiContentSelect => _guiContentSelectCache = _guiContentSelectCache ?? new GUIContent("", AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath(_iconSelectGUID)), _iconSelectTooltip);
|
||||
private static GUIContent _guiContentChechout => _guiContentCheckoutCache = _guiContentCheckoutCache ?? new GUIContent("", AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath(_iconCheckoutGUID)), _iconCheckoutTooltip);
|
||||
private static GUIContent _guiContentExpand => _guiContentExpandCache = _guiContentExpandCache ?? new GUIContent("", AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath(_iconExpandGUID)), _iconExpandTooltip);
|
||||
private static GUIContent _guiContentCollapse => _guiContentCollapseCache = _guiContentCollapseCache ?? new GUIContent("", AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath(_iconCollapseGUID)), _iconCollapseTooltip);
|
||||
private static GUIContent _guiContentStats => _guiContentStatsCache = _guiContentStatsCache ?? new GUIContent("", AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath(_iconStatsGUID)), _iconStatsTooltip);
|
||||
private static GUIContent _guiContentVisibility => _guiContentVisibilityCache = _guiContentVisibilityCache ?? new GUIContent("", AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath(_iconVisibilityGUID)), _iconVisibilityTooltip);
|
||||
|
||||
|
||||
public static void DrawToolbarButtons(ref Rect toolBarRect, LWGUIMetaDatas metaDatas)
|
||||
{
|
||||
var (perShaderData, perMaterialData, perInspectorData) = metaDatas.GetDatas();
|
||||
var shader = metaDatas.GetShader();
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
// Copy
|
||||
var buttonRectOffset = toolBarRect.height + 2;
|
||||
var buttonRect = new Rect(toolBarRect.x, toolBarRect.y, toolBarRect.height, toolBarRect.height);
|
||||
toolBarRect.xMin += buttonRectOffset;
|
||||
if (GUI.Button(buttonRect, _guiContentCopy, GUIStyles.iconButton))
|
||||
{
|
||||
ContextMenuHelper.CopyMaterial(metaDatas.GetMaterial());
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
// Paste
|
||||
buttonRect.x += buttonRectOffset;
|
||||
toolBarRect.xMin += buttonRectOffset;
|
||||
// Right Click
|
||||
if (Event.current.type == EventType.MouseDown
|
||||
&& Event.current.button == 1
|
||||
&& buttonRect.Contains(Event.current.mousePosition))
|
||||
{
|
||||
EditorUtility.DisplayCustomMenu(new Rect(Event.current.mousePosition.x, Event.current.mousePosition.y, 0, 0), _pasteMaterialMenus, -1,
|
||||
(data, options, selected) => { ContextMenuHelper.PastePropertiesToMaterials(metaDatas, _pasteMaterialMenuValueMasks[selected]); }, null);
|
||||
Event.current.Use();
|
||||
}
|
||||
// Left Click
|
||||
if (GUI.Button(buttonRect, _guiContentPaste, GUIStyles.iconButton))
|
||||
{
|
||||
ContextMenuHelper.PastePropertiesToMaterials(metaDatas, (uint)CopyMaterialValueMask.All);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
// Select Material Asset, jump from a Runtime Material Instance to a Material Asset
|
||||
buttonRect.x += buttonRectOffset;
|
||||
toolBarRect.xMin += buttonRectOffset;
|
||||
if (GUI.Button(buttonRect, _guiContentSelect, GUIStyles.iconButton))
|
||||
{
|
||||
var material = metaDatas.GetMaterial();
|
||||
|
||||
if (AssetDatabase.Contains(material))
|
||||
{
|
||||
Selection.activeObject = material;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FindMaterialAssetByMaterialInstance(material, metaDatas, out var materialAsset))
|
||||
{
|
||||
Selection.activeObject = materialAsset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
// Checkout
|
||||
buttonRect.x += buttonRectOffset;
|
||||
toolBarRect.xMin += buttonRectOffset;
|
||||
if (GUI.Button(buttonRect, _guiContentChechout, GUIStyles.iconButton))
|
||||
{
|
||||
VersionControlHelper.Checkout(metaDatas.GetMaterialEditor().targets);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
// Expand
|
||||
buttonRect.x += buttonRectOffset;
|
||||
toolBarRect.xMin += buttonRectOffset;
|
||||
if (GUI.Button(buttonRect, _guiContentExpand, GUIStyles.iconButton))
|
||||
{
|
||||
foreach (var propStaticDataKVPair in perShaderData.propStaticDatas)
|
||||
{
|
||||
if (propStaticDataKVPair.Value.isMain || propStaticDataKVPair.Value.isAdvancedHeader)
|
||||
propStaticDataKVPair.Value.isExpanding = true;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
// Collapse
|
||||
buttonRect.x += buttonRectOffset;
|
||||
toolBarRect.xMin += buttonRectOffset;
|
||||
if (GUI.Button(buttonRect, _guiContentCollapse, GUIStyles.iconButton))
|
||||
{
|
||||
foreach (var propStaticDataKVPair in perShaderData.propStaticDatas)
|
||||
{
|
||||
if (propStaticDataKVPair.Value.isMain || propStaticDataKVPair.Value.isAdvancedHeader)
|
||||
propStaticDataKVPair.Value.isExpanding = false;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
// Shader Perf Stats
|
||||
buttonRect.x += buttonRectOffset;
|
||||
toolBarRect.xMin += buttonRectOffset;
|
||||
{
|
||||
var color = GUI.color;
|
||||
if (IsDisplayShaderPerfStatsEnabled(metaDatas.GetShaderUID()))
|
||||
GUI.color = Color.yellow;
|
||||
|
||||
if (GUI.Button(buttonRect, _guiContentStats, GUIStyles.iconButton))
|
||||
SwitchDisplayShaderPerfStatsEnabled(shader, metaDatas.GetShaderUID());
|
||||
|
||||
GUI.color = color;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
// Display Mode
|
||||
buttonRect.x += buttonRectOffset;
|
||||
toolBarRect.xMin += buttonRectOffset;
|
||||
{
|
||||
var color = GUI.color;
|
||||
var displayModeData = perShaderData.displayModeData;
|
||||
if (!displayModeData.IsDefaultDisplayMode())
|
||||
GUI.color = Color.yellow;
|
||||
if (GUI.Button(buttonRect, _guiContentVisibility, GUIStyles.iconButton))
|
||||
{
|
||||
// Build Display Mode Menu Items
|
||||
var displayModeMenus = new[]
|
||||
{
|
||||
$"Show All Advanced Properties ({displayModeData.advancedCount} - {perShaderData.propStaticDatas.Count})",
|
||||
$"Show All Hidden Properties ({displayModeData.hiddenCount} - {perShaderData.propStaticDatas.Count})",
|
||||
$"Show Only Modified Properties ({perMaterialData.modifiedCount} - {perShaderData.propStaticDatas.Count})",
|
||||
$"Show Only Modified Properties by Group ({perMaterialData.modifiedCount} - {perShaderData.propStaticDatas.Count})",
|
||||
};
|
||||
var enabled = new[] { true, true, true, true };
|
||||
var separator = new bool[4];
|
||||
var selected = new[]
|
||||
{
|
||||
displayModeData.showAllAdvancedProperties ? 0 : -1,
|
||||
displayModeData.showAllHiddenProperties ? 1 : -1,
|
||||
displayModeData.showOnlyModifiedProperties ? 2 : -1,
|
||||
displayModeData.showOnlyModifiedGroups ? 3 : -1,
|
||||
};
|
||||
|
||||
|
||||
// Click Event
|
||||
void OnSwitchDisplayMode(object data, string[] options, int selectedIndex)
|
||||
{
|
||||
switch (selectedIndex)
|
||||
{
|
||||
case 0: // Show All Advanced Properties
|
||||
displayModeData.showAllAdvancedProperties = !displayModeData.showAllAdvancedProperties;
|
||||
perShaderData.ToggleShowAllAdvancedProperties();
|
||||
break;
|
||||
case 1: // Show All Hidden Properties
|
||||
displayModeData.showAllHiddenProperties = !displayModeData.showAllHiddenProperties;
|
||||
break;
|
||||
case 2: // Show Only Modified Properties
|
||||
displayModeData.showOnlyModifiedProperties = !displayModeData.showOnlyModifiedProperties;
|
||||
if (displayModeData.showOnlyModifiedProperties) displayModeData.showOnlyModifiedGroups = false;
|
||||
MetaDataHelper.ForceUpdateAllMaterialsMetadataCache(shader);
|
||||
break;
|
||||
case 3: // Show Only Modified Groups
|
||||
displayModeData.showOnlyModifiedGroups = !displayModeData.showOnlyModifiedGroups;
|
||||
if (displayModeData.showOnlyModifiedGroups) displayModeData.showOnlyModifiedProperties = false;
|
||||
MetaDataHelper.ForceUpdateAllMaterialsMetadataCache(shader);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ReflectionHelper.DisplayCustomMenuWithSeparators(new Rect(Event.current.mousePosition.x, Event.current.mousePosition.y, 0, 0),
|
||||
displayModeMenus, enabled, separator, selected, OnSwitchDisplayMode);
|
||||
}
|
||||
GUI.color = color;
|
||||
}
|
||||
|
||||
toolBarRect.xMin += 2;
|
||||
}
|
||||
|
||||
public static Func<Renderer, Material, Material> onFindMaterialAssetInRendererByMaterialInstance;
|
||||
|
||||
public static bool FindMaterialAsset(LWGUIMetaDatas metaDatas, out Material materialAsset)
|
||||
{
|
||||
return FindMaterialAssetByMaterialInstance(metaDatas.GetMaterial(), metaDatas, out materialAsset);
|
||||
}
|
||||
|
||||
private static bool FindMaterialAssetByMaterialInstance(Material material, LWGUIMetaDatas metaDatas, out Material materialAsset)
|
||||
{
|
||||
materialAsset = null;
|
||||
|
||||
var renderers = metaDatas.perInspectorData.materialEditor.GetMeshRenderersByMaterialEditor();
|
||||
foreach (var renderer in renderers)
|
||||
{
|
||||
if (onFindMaterialAssetInRendererByMaterialInstance != null)
|
||||
{
|
||||
materialAsset = onFindMaterialAssetInRendererByMaterialInstance(renderer, material);
|
||||
}
|
||||
|
||||
if (materialAsset == null)
|
||||
{
|
||||
int index = renderer.materials.ToList().FindIndex(materialInstance => materialInstance == material);
|
||||
if (index >= 0 && index < renderer.sharedMaterials.Length)
|
||||
{
|
||||
materialAsset = renderer.sharedMaterials[index];
|
||||
}
|
||||
}
|
||||
|
||||
if (materialAsset != null && AssetDatabase.Contains(materialAsset))
|
||||
return true;
|
||||
}
|
||||
|
||||
Debug.LogError("LWGUI: Can not find the Material Assets of: " + material.name);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Search Field
|
||||
|
||||
private static readonly int s_TextFieldHash = "EditorTextField".GetHashCode();
|
||||
|
||||
private static readonly GUIContent[] _searchModeMenus = Enumerable.Range(0, (int)SearchMode.Num - 1).Select(i =>
|
||||
new GUIContent(((SearchMode)i).ToString())).ToArray();
|
||||
|
||||
/// <returns>is has changed?</returns>
|
||||
public static bool DrawSearchField(Rect rect, LWGUIMetaDatas metaDatas)
|
||||
{
|
||||
var (perShaderData, perMaterialData, perInspectorData) = metaDatas.GetDatas();
|
||||
|
||||
bool hasChanged = false;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
var revertButtonRect = RevertableHelper.SplitRevertButtonRect(ref rect);
|
||||
|
||||
// Get internal TextField ControlID
|
||||
int controlId = GUIUtility.GetControlID(s_TextFieldHash, FocusType.Keyboard, rect) + 1;
|
||||
|
||||
// searching mode
|
||||
Rect modeRect = new Rect(rect);
|
||||
modeRect.width = 20f;
|
||||
if (Event.current.type == UnityEngine.EventType.MouseDown && modeRect.Contains(Event.current.mousePosition))
|
||||
{
|
||||
EditorUtility.DisplayCustomMenu(rect, _searchModeMenus, (int)perShaderData.searchMode,
|
||||
(data, options, selected) =>
|
||||
{
|
||||
perShaderData.searchMode = (SearchMode)selected;
|
||||
hasChanged = true;
|
||||
}, null);
|
||||
Event.current.Use();
|
||||
}
|
||||
|
||||
perShaderData.searchString = EditorGUI.TextField(rect, String.Empty, perShaderData.searchString, GUIStyles.toolbarSearchTextFieldPopup);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
hasChanged = true;
|
||||
|
||||
// revert button
|
||||
if (!string.IsNullOrEmpty(perShaderData.searchString)
|
||||
&& RevertableHelper.DrawRevertButton(revertButtonRect))
|
||||
{
|
||||
perShaderData.searchString = string.Empty;
|
||||
hasChanged = true;
|
||||
GUIUtility.keyboardControl = 0;
|
||||
}
|
||||
|
||||
// display search mode
|
||||
if (GUIUtility.keyboardControl != controlId
|
||||
&& string.IsNullOrEmpty(perShaderData.searchString)
|
||||
&& Event.current.type == UnityEngine.EventType.Repaint)
|
||||
{
|
||||
using (new EditorGUI.DisabledScope(true))
|
||||
{
|
||||
var disableTextRect = new Rect(rect.x, rect.y, rect.width,
|
||||
GUIStyles.toolbarSearchTextFieldPopup.fixedHeight > 0.0
|
||||
? GUIStyles.toolbarSearchTextFieldPopup.fixedHeight
|
||||
: rect.height);
|
||||
disableTextRect = GUIStyles.toolbarSearchTextFieldPopup.padding.Remove(disableTextRect);
|
||||
int fontSize = EditorStyles.label.fontSize;
|
||||
EditorStyles.label.fontSize = GUIStyles.toolbarSearchTextFieldPopup.fontSize;
|
||||
EditorStyles.label.Draw(disableTextRect, new GUIContent(perShaderData.searchMode.ToString()), false, false, false, false);
|
||||
EditorStyles.label.fontSize = fontSize;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasChanged) perShaderData.UpdateSearchFilter();
|
||||
|
||||
return hasChanged;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Shader Perf Stats
|
||||
|
||||
#region Keyword Overrides
|
||||
|
||||
private static string GetShowKeywordOverridesPreferenceKey(string shaderUID) => $"LWGUI/{shaderUID}/ShowKeywordOverrides";
|
||||
|
||||
private static string GetKeywordOverridePreferenceKey(string shaderUID, string keyword) => $"LWGUI/{shaderUID}/KeywordOverride/{keyword}/IsOverride";
|
||||
|
||||
private static string GetKeywordEnabledPreferenceKey(string shaderUID, string keyword) => $"LWGUI/{shaderUID}/KeywordOverride/{keyword}/IsEnabled";
|
||||
|
||||
private static bool IsShowKeywordOverridesEnabled(string shaderUID) => EditorPrefs.GetBool(GetShowKeywordOverridesPreferenceKey(shaderUID), false);
|
||||
|
||||
public static bool IsUserKeywordOverride(string shaderUID, string keyword) => EditorPrefs.HasKey(GetKeywordOverridePreferenceKey(shaderUID, keyword));
|
||||
|
||||
public static bool IsUserKeywordEnabled(string shaderUID, string keyword) => EditorPrefs.GetBool(GetKeywordEnabledPreferenceKey(shaderUID, keyword), false);
|
||||
|
||||
private static void SetShowKeywordOverridesEnabled(string shaderUID, bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
EditorPrefs.SetBool(GetShowKeywordOverridesPreferenceKey(shaderUID), true);
|
||||
else
|
||||
EditorPrefs.DeleteKey(GetShowKeywordOverridesPreferenceKey(shaderUID));
|
||||
}
|
||||
|
||||
private static void SetUserKeywordOverride(Shader shader, string shaderUID, string keyword, bool isOverride)
|
||||
{
|
||||
var overrideKey = GetKeywordOverridePreferenceKey(shaderUID, keyword);
|
||||
|
||||
if (isOverride)
|
||||
EditorPrefs.SetBool(overrideKey, true);
|
||||
else
|
||||
EditorPrefs.DeleteKey(overrideKey);
|
||||
|
||||
MetaDataHelper.ForceUpdateAllMaterialsMetadataCache(shader);
|
||||
}
|
||||
|
||||
private static void SetUserKeywordEnabled(Shader shader, string shaderUID, string keyword, bool isEnabled)
|
||||
{
|
||||
EditorPrefs.SetBool(GetKeywordEnabledPreferenceKey(shaderUID, keyword), isEnabled);
|
||||
MetaDataHelper.ForceUpdateAllMaterialsMetadataCache(shader);
|
||||
}
|
||||
|
||||
private static void DrawKeywordOverridesList(LWGUIMetaDatas metaDatas)
|
||||
{
|
||||
var shader = metaDatas.GetShader();
|
||||
var shaderUID = metaDatas.GetShaderUID();
|
||||
if (!shader) return;
|
||||
|
||||
var showKeywordOverrides = IsShowKeywordOverridesEnabled(shaderUID);
|
||||
|
||||
EditorGUI.indentLevel++;
|
||||
var newShowKeywordOverrides = EditorGUILayout.Foldout(showKeywordOverrides, "Keyword Overrides");
|
||||
if (newShowKeywordOverrides != showKeywordOverrides)
|
||||
SetShowKeywordOverridesEnabled(shaderUID, newShowKeywordOverrides);
|
||||
if (newShowKeywordOverrides)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Enabled");
|
||||
EditorGUILayout.LabelField(" Override");
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
var activeKeywords = metaDatas.perMaterialData.activeKeywords;
|
||||
var allKeywords = shader.keywordSpace.keywords.Select(k => k.name).ToList();
|
||||
foreach (var keyword in allKeywords)
|
||||
{
|
||||
var rect = EditorGUILayout.BeginHorizontal();
|
||||
|
||||
// Context Menu
|
||||
if (Event.current.type == EventType.ContextClick && rect.Contains(Event.current.mousePosition))
|
||||
{
|
||||
Event.current.Use();
|
||||
var menu = new GenericMenu();
|
||||
menu.AddItem(new GUIContent("Copy Keyword"), false, () =>
|
||||
{
|
||||
EditorGUIUtility.systemCopyBuffer = keyword;
|
||||
});
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
|
||||
bool currentOverride = IsUserKeywordOverride(shaderUID, keyword);
|
||||
bool currentEnabled = currentOverride ? IsUserKeywordEnabled(shaderUID, keyword) : activeKeywords.Contains(keyword);
|
||||
|
||||
using (new EditorGUI.DisabledGroupScope(!currentOverride))
|
||||
{
|
||||
var newEnabled = EditorGUILayout.ToggleLeft(keyword, currentEnabled);
|
||||
if (newEnabled != currentEnabled)
|
||||
SetUserKeywordEnabled(shader, shaderUID, keyword, newEnabled);
|
||||
}
|
||||
|
||||
bool newOverride = EditorGUILayout.Toggle(string.Empty, currentOverride);
|
||||
if (newOverride != currentOverride)
|
||||
SetUserKeywordOverride(shader, shaderUID, keyword, newOverride);
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Compiler Settings
|
||||
|
||||
private static string GetShowCompilerSettingsPreferenceKey(string shaderUID) => $"LWGUI/{shaderUID}/ShowCompilerSettings";
|
||||
private static bool IsShowCompilerSettingsEnabled(string shaderUID) => EditorPrefs.GetBool(GetShowCompilerSettingsPreferenceKey(shaderUID), false);
|
||||
private static void SetShowCompilerSettingsEnabled(string shaderUID, bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
EditorPrefs.SetBool(GetShowCompilerSettingsPreferenceKey(shaderUID), true);
|
||||
else
|
||||
EditorPrefs.DeleteKey(GetShowCompilerSettingsPreferenceKey(shaderUID));
|
||||
}
|
||||
|
||||
private static void DrawCompilerSettings(LWGUIMetaDatas metaDatas)
|
||||
{
|
||||
var shaderUID = metaDatas.GetShaderUID();
|
||||
var showCompilerSettings = IsShowCompilerSettingsEnabled(shaderUID);
|
||||
|
||||
EditorGUI.indentLevel++;
|
||||
var newShowCompilerSettings = EditorGUILayout.Foldout(showCompilerSettings, "Compiler Settings");
|
||||
if (newShowCompilerSettings != showCompilerSettings)
|
||||
SetShowCompilerSettingsEnabled(shaderUID, newShowCompilerSettings);
|
||||
if (newShowCompilerSettings)
|
||||
{
|
||||
if (GUILayout.Button("Install Mali Offline Compiler", GUILayout.ExpandWidth(false)))
|
||||
{
|
||||
Application.OpenURL("https://developer.arm.com/documentation/101863/8-8/Using-Mali-Offline-Compiler/Install-Mali-Offline-Compiler");
|
||||
}
|
||||
}
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
#endregion
|
||||
private static string GetDisplayShaderPerfStatsPreferenceKey(string shaderUID) => $"LWGUI/{shaderUID}/DisplayShaderPerformanceStats";
|
||||
|
||||
public static bool IsDisplayShaderPerfStatsEnabled(string shaderUID) => EditorPrefs.HasKey(GetDisplayShaderPerfStatsPreferenceKey(shaderUID));
|
||||
|
||||
public static void SetDisplayShaderPerfStatsEnabled(Shader shader, string shaderUID, bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
EditorPrefs.SetBool(GetDisplayShaderPerfStatsPreferenceKey(shaderUID), true);
|
||||
else
|
||||
EditorPrefs.DeleteKey(GetDisplayShaderPerfStatsPreferenceKey(shaderUID));
|
||||
MetaDataHelper.ForceUpdateAllMaterialsMetadataCache(shader);
|
||||
}
|
||||
|
||||
public static void SwitchDisplayShaderPerfStatsEnabled(Shader shader, string shaderUID)
|
||||
{
|
||||
var key = GetDisplayShaderPerfStatsPreferenceKey(shaderUID);
|
||||
if (EditorPrefs.HasKey(key))
|
||||
EditorPrefs.DeleteKey(key);
|
||||
else
|
||||
EditorPrefs.SetBool(key, true);
|
||||
MetaDataHelper.ForceUpdateAllMaterialsMetadataCache(shader);
|
||||
}
|
||||
|
||||
public static void DrawShaderPerformanceStats(LWGUIMetaDatas metaDatas)
|
||||
{
|
||||
if (!IsDisplayShaderPerfStatsEnabled(metaDatas.GetShaderUID()) || metaDatas.perMaterialData.shaderPerfDatas == null)
|
||||
return;
|
||||
|
||||
var fieldWidth = EditorGUIUtility.fieldWidth;
|
||||
EditorGUIUtility.fieldWidth = 0;
|
||||
|
||||
var compiler = ShaderPerfMonitor.GetActiveCompiler();
|
||||
if (compiler != null)
|
||||
EditorGUILayout.LabelField($"Shader Performance Stats (Compiler: {compiler?.compilerName ?? "NULL"}, API: {compiler.api}, Target: {compiler.target})", GUIStyles.title);
|
||||
else
|
||||
EditorGUILayout.LabelField($"Shader Performance Stats (Compiler: NULL)", GUIStyles.title);
|
||||
|
||||
if (compiler != null)
|
||||
{
|
||||
DrawCompilerSettings(metaDatas);
|
||||
DrawKeywordOverridesList(metaDatas);
|
||||
compiler.DrawShaderPerformanceStatsHeader(metaDatas);
|
||||
|
||||
var lastPassName = string.Empty;
|
||||
foreach (var shaderPerfData in metaDatas.perMaterialData.shaderPerfDatas)
|
||||
{
|
||||
if (lastPassName == string.Empty)
|
||||
lastPassName = shaderPerfData.passName;
|
||||
|
||||
if (lastPassName != shaderPerfData.passName)
|
||||
{
|
||||
lastPassName = shaderPerfData.passName;
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
|
||||
compiler.DrawShaderPerformanceStatsLine(metaDatas, shaderPerfData);
|
||||
}
|
||||
compiler.DrawShaderPerformanceStatsFooter(metaDatas);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("LWGUI: Can NOT get Shader Compiler!");
|
||||
}
|
||||
|
||||
EditorGUIUtility.fieldWidth = fieldWidth;
|
||||
EditorGUILayout.Space();
|
||||
Helper.DrawSplitLine();
|
||||
}
|
||||
|
||||
|
||||
public static void DrawShaderPerformanceStatsLineButtons(ShaderPerfData shaderPerfData)
|
||||
{
|
||||
if (GUILayout.Button("Find", GUILayout.MaxWidth(40)))
|
||||
EditorUtility.RevealInFinder(shaderPerfData.compiledShaderPath);
|
||||
// if (GUILayout.Button("Open", GUILayout.MaxWidth(40)))
|
||||
// IOHelper.OpenFile(shaderPerfData.compiledShaderPath);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a055ea55b4f0432d872562e7c2e2c296
|
||||
timeCreated: 1760781507
|
||||
@ -1,13 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 72488141525eaa8499e65e52755cb6d0
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 4
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
@ -20,7 +20,12 @@ TextureImporter:
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
@ -51,21 +56,32 @@ TextureImporter:
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 2
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 1
|
||||
swizzle: 50462976
|
||||
cookieLightType: 1
|
||||
platformSettings:
|
||||
- buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
- buildTarget: Standalone
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 128
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
@ -74,13 +90,25 @@ TextureImporter:
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
spritePackingTag:
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 929b6e5dfacc42b429d715a3e1ca2b57
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 4
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
@ -20,7 +20,12 @@ TextureImporter:
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
@ -51,21 +56,32 @@ TextureImporter:
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 2
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 1
|
||||
swizzle: 50462976
|
||||
cookieLightType: 1
|
||||
platformSettings:
|
||||
- buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
- buildTarget: Standalone
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 128
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
@ -74,13 +90,25 @@ TextureImporter:
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
spritePackingTag:
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9cdef444d18d2ce4abb6bbc4fed4d109
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 4
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
@ -20,7 +20,12 @@ TextureImporter:
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
@ -51,21 +56,32 @@ TextureImporter:
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 2
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 1
|
||||
swizzle: 50462976
|
||||
cookieLightType: 1
|
||||
platformSettings:
|
||||
- buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
- buildTarget: Standalone
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 128
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
@ -74,13 +90,25 @@ TextureImporter:
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
spritePackingTag:
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2382450e7f4ddb94c9180d6634c41378
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 4
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
@ -20,7 +20,12 @@ TextureImporter:
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
@ -51,21 +56,32 @@ TextureImporter:
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 2
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 1
|
||||
swizzle: 50462976
|
||||
cookieLightType: 1
|
||||
platformSettings:
|
||||
- buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
- buildTarget: Standalone
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 128
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
@ -74,13 +90,25 @@ TextureImporter:
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
spritePackingTag:
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
@ -3,7 +3,7 @@ guid: 2a220687c380819438534e206c94960d
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 11
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
@ -20,11 +20,12 @@ TextureImporter:
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMasterTextureLimit: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
@ -63,10 +64,12 @@ TextureImporter:
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 1
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 8192
|
||||
maxTextureSize: 256
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
@ -74,6 +77,7 @@ TextureImporter:
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
@ -86,6 +90,7 @@ TextureImporter:
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
@ -98,6 +103,7 @@ TextureImporter:
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
@ -110,6 +116,7 @@ TextureImporter:
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
@ -122,6 +129,7 @@ TextureImporter:
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
@ -134,6 +142,7 @@ TextureImporter:
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
@ -150,9 +159,8 @@ TextureImporter:
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
spritePackingTag:
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
@ -1,14 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 26b9d845eb7b1a747bf04dc84e5bcc2c
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 4
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
mipMapLayout: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
@ -21,8 +20,12 @@ TextureImporter:
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 1
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
@ -31,12 +34,12 @@ TextureImporter:
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: -1
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: -1
|
||||
mipBias: 0
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: -1
|
||||
wrapW: 0
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
@ -53,11 +56,32 @@ TextureImporter:
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 2
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 1
|
||||
swizzle: 50462976
|
||||
cookieLightType: 1
|
||||
platformSettings:
|
||||
- buildTarget: DefaultTexturePlatform
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 256
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
@ -66,9 +90,11 @@ TextureImporter:
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
platformFilterMode: 1
|
||||
- buildTarget: Standalone
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: iPhone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
@ -77,9 +103,11 @@ TextureImporter:
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
platformFilterMode: 1
|
||||
- buildTarget: iPhone
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 1
|
||||
- serializedVersion: 3
|
||||
buildTarget: tvOS
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
@ -88,9 +116,11 @@ TextureImporter:
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
platformFilterMode: 1
|
||||
- buildTarget: tvOS
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 1
|
||||
- serializedVersion: 3
|
||||
buildTarget: Android
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
@ -99,9 +129,11 @@ TextureImporter:
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
platformFilterMode: 1
|
||||
- buildTarget: Android
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 1
|
||||
- serializedVersion: 3
|
||||
buildTarget: WebGL
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
@ -110,9 +142,11 @@ TextureImporter:
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
platformFilterMode: 1
|
||||
- buildTarget: WebGL
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 1
|
||||
- serializedVersion: 3
|
||||
buildTarget: PS4
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
@ -121,27 +155,25 @@ TextureImporter:
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
platformFilterMode: 1
|
||||
- buildTarget: PS4
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
platformFilterMode: 1
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 1
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
spritePackingTag:
|
||||
useMipMapOverrideTexture: 0
|
||||
mipMapOverrideTextureNames: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8e7a78d02e4c3574998524a0842a8ccb
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 4
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
@ -20,7 +20,12 @@ TextureImporter:
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
@ -51,21 +56,32 @@ TextureImporter:
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 2
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 1
|
||||
swizzle: 50462976
|
||||
cookieLightType: 1
|
||||
platformSettings:
|
||||
- buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
- buildTarget: Standalone
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 128
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
@ -74,13 +90,25 @@ TextureImporter:
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
spritePackingTag:
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
@ -3,7 +3,7 @@ guid: e7bc1130858d984488bca32b8512ca96
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 11
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
@ -20,11 +20,12 @@ TextureImporter:
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMasterTextureLimit: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
@ -63,10 +64,12 @@ TextureImporter:
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 1
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 8192
|
||||
maxTextureSize: 128
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
@ -74,6 +77,7 @@ TextureImporter:
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
@ -86,6 +90,7 @@ TextureImporter:
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
@ -98,6 +103,7 @@ TextureImporter:
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
@ -110,6 +116,7 @@ TextureImporter:
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
@ -122,6 +129,7 @@ TextureImporter:
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
@ -134,6 +142,7 @@ TextureImporter:
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
@ -150,9 +159,8 @@ TextureImporter:
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
spritePackingTag:
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f44e40b24300974eb607293e4224ecc
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 4
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
@ -20,7 +20,12 @@ TextureImporter:
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
@ -51,21 +56,32 @@ TextureImporter:
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 2
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 1
|
||||
swizzle: 50462976
|
||||
cookieLightType: 1
|
||||
platformSettings:
|
||||
- buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
- buildTarget: Standalone
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 128
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
@ -74,13 +90,25 @@ TextureImporter:
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
spritePackingTag:
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
BIN
Assets/NiloToonURP/Editor/ShaderGUI/LWGUI-main/Editor/Icon/Stats.png
(Stored with Git LFS)
Normal file
BIN
Assets/NiloToonURP/Editor/ShaderGUI/LWGUI-main/Editor/Icon/Stats.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -0,0 +1,114 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 88909414120107547a673b8fcddc5236
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
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
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 0
|
||||
nPOTScale: 0
|
||||
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: 2
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 128
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 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
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -1,13 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9576e23a695b35d49a9fc55c9a948b4f
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 4
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
@ -20,7 +20,12 @@ TextureImporter:
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
@ -29,12 +34,12 @@ TextureImporter:
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: -1
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: -1
|
||||
mipBias: 0
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: -1
|
||||
wrapW: 0
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
@ -51,21 +56,32 @@ TextureImporter:
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 2
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 1
|
||||
swizzle: 50462976
|
||||
cookieLightType: 1
|
||||
platformSettings:
|
||||
- buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
- buildTarget: Standalone
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 128
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
@ -74,13 +90,25 @@ TextureImporter:
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
spritePackingTag:
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
@ -4,7 +4,8 @@
|
||||
"references": [
|
||||
"LWGUI.Runtime",
|
||||
"LWGUI.Timeline",
|
||||
"Unity.InternalAPIEditorBridge.020"
|
||||
"Unity.InternalAPIEditorBridge.020",
|
||||
"LWGUI.Runtime.Timeline"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright (c) Jason Ma
|
||||
// Copyright (c) Jason Ma
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
@ -7,6 +7,7 @@ using UnityEngine.Rendering;
|
||||
namespace LWGUI
|
||||
{
|
||||
public delegate void LWGUICustomGUIEvent(LWGUI lwgui);
|
||||
public delegate void LWGUIToolbarExtensionEvent(LWGUI lwgui, ref Rect toolBarRect);
|
||||
|
||||
public class LWGUI : ShaderGUI
|
||||
{
|
||||
@ -15,6 +16,8 @@ namespace LWGUI
|
||||
|
||||
public static LWGUICustomGUIEvent onDrawCustomHeader;
|
||||
public static LWGUICustomGUIEvent onDrawCustomFooter;
|
||||
public static LWGUIToolbarExtensionEvent onDrawToolbarLeft;
|
||||
public static LWGUIToolbarExtensionEvent onDrawToolbarRight;
|
||||
|
||||
/// <summary>
|
||||
/// Called when switch to a new Material Window, each window has a LWGUI instance
|
||||
@ -44,19 +47,24 @@ namespace LWGUI
|
||||
onDrawCustomHeader(this);
|
||||
|
||||
// Toolbar
|
||||
bool enabled = GUI.enabled;
|
||||
GUI.enabled = true;
|
||||
var toolBarRect = EditorGUILayout.GetControlRect();
|
||||
toolBarRect.xMin = 2;
|
||||
{
|
||||
bool enabled = GUI.enabled;
|
||||
GUI.enabled = true;
|
||||
var toolBarRect = EditorGUILayout.GetControlRect();
|
||||
toolBarRect.xMin = 2;
|
||||
|
||||
Helper.DrawToolbarButtons(ref toolBarRect, metaDatas);
|
||||
onDrawToolbarLeft?.Invoke(this, ref toolBarRect);
|
||||
ToolbarHelper.DrawToolbarButtons(ref toolBarRect, metaDatas);
|
||||
onDrawToolbarRight?.Invoke(this, ref toolBarRect);
|
||||
ToolbarHelper.DrawSearchField(toolBarRect, metaDatas);
|
||||
|
||||
Helper.DrawSearchField(toolBarRect, metaDatas);
|
||||
|
||||
GUILayoutUtility.GetRect(0, 0); // Space(0)
|
||||
GUI.enabled = enabled;
|
||||
Helper.DrawSplitLine();
|
||||
GUILayoutUtility.GetRect(0, 0); // Space(0)
|
||||
GUI.enabled = enabled;
|
||||
Helper.DrawSplitLine();
|
||||
|
||||
ToolbarHelper.DrawShaderPerformanceStats(metaDatas);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Draw Properties
|
||||
@ -147,7 +155,7 @@ namespace LWGUI
|
||||
if (Event.current.type == EventType.MouseDown && Event.current.button == 0 && rect.Contains(Event.current.mousePosition))
|
||||
propStaticData.isExpanding = !propStaticData.isExpanding;
|
||||
RevertableHelper.DrawRevertableProperty(revertButtonRect, prop, metaDatas, true);
|
||||
Helper.DoPropertyContextMenus(rect, prop, metaDatas);
|
||||
ContextMenuHelper.DoPropertyContextMenus(rect, prop, metaDatas);
|
||||
}
|
||||
|
||||
private void DrawProperty(MaterialProperty prop)
|
||||
@ -167,9 +175,10 @@ namespace LWGUI
|
||||
var revertButtonRect = RevertableHelper.SplitRevertButtonRect(ref rect);
|
||||
|
||||
var enabled = GUI.enabled;
|
||||
if (propStaticData.isReadOnly) GUI.enabled = false;
|
||||
if (propStaticData.isReadOnly || !propDynamicData.isActive)
|
||||
GUI.enabled = false;
|
||||
Helper.BeginProperty(rect, prop, metaDatas);
|
||||
Helper.DoPropertyContextMenus(rect, prop, metaDatas);
|
||||
ContextMenuHelper.DoPropertyContextMenus(rect, prop, metaDatas);
|
||||
|
||||
RevertableHelper.FixGUIWidthMismatch(prop.GetPropertyType(), materialEditor);
|
||||
if (propStaticData.isAdvancedHeaderProperty)
|
||||
@ -229,5 +238,19 @@ namespace LWGUI
|
||||
if (!hasChange) hasChange = true;
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("CONTEXT/Material/Reimport Shader", false, 100)]
|
||||
static void MenuItem_ReimportShader(MenuCommand command)
|
||||
{
|
||||
var mat = command.context as Material;
|
||||
if (mat != null)
|
||||
{
|
||||
var path = AssetDatabase.GetAssetPath(mat.shader);
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
AssetDatabase.ImportAsset(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,212 +1,273 @@
|
||||
// Copyright (c) Jason Ma
|
||||
// Copyright (c) Jason Ma
|
||||
// Per Shader > Per Material > Per Inspector
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using LWGUI.PerformanceMonitor;
|
||||
|
||||
namespace LWGUI
|
||||
{
|
||||
public class PersetDynamicData
|
||||
{
|
||||
public LwguiShaderPropertyPreset.Preset preset;
|
||||
public MaterialProperty property;
|
||||
public class PresetDynamicData
|
||||
{
|
||||
public LwguiShaderPropertyPreset.Preset preset;
|
||||
public MaterialProperty property;
|
||||
|
||||
public PersetDynamicData(LwguiShaderPropertyPreset.Preset preset, MaterialProperty property)
|
||||
{
|
||||
this.preset = preset;
|
||||
this.property = property;
|
||||
}
|
||||
}
|
||||
public PresetDynamicData(LwguiShaderPropertyPreset.Preset preset, MaterialProperty property)
|
||||
{
|
||||
this.preset = preset;
|
||||
this.property = property;
|
||||
}
|
||||
}
|
||||
|
||||
public class PropertyDynamicData
|
||||
{
|
||||
public MaterialProperty property;
|
||||
public MaterialProperty defualtProperty; // Default values may be overridden by Preset
|
||||
public class PropertyDynamicData
|
||||
{
|
||||
public MaterialProperty property;
|
||||
public MaterialProperty defaultProperty; // Default values may be overridden by Preset
|
||||
|
||||
public string defaultValueDescription = string.Empty; // Description of the default values used in Tooltip
|
||||
public bool hasModified = false; // Are properties modified in the material?
|
||||
public bool hasChildrenModified = false; // Are Children properties modified in the material?
|
||||
public bool hasRevertChanged = false; // Used to call property EndChangeCheck()
|
||||
public bool isShowing = true; // ShowIf() result
|
||||
public bool isAnimated = false; // Material Parameter Animation preview in Timeline is activated
|
||||
}
|
||||
public string defaultValueDescription = string.Empty; // Description of the default values used in Tooltip
|
||||
public bool hasModified = false; // Are properties modified in the material?
|
||||
public bool hasChildrenModified = false; // Are Children properties modified in the material?
|
||||
public bool hasRevertChanged = false; // Used to call property EndChangeCheck()
|
||||
public bool isShowing = true; // ShowIf() result
|
||||
public bool isActive = true; // ActiveIf() result
|
||||
public bool isAnimated = false; // Material Parameter Animation preview in Timeline is activated
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains Metadata that may be different for each Material.
|
||||
/// </summary>
|
||||
public class PerMaterialData
|
||||
{
|
||||
public Dictionary<string, PropertyDynamicData> propDynamicDatas = new Dictionary<string, PropertyDynamicData>();
|
||||
public MaterialProperty[] props = null;
|
||||
public Material material = null;
|
||||
public Material defaultMaterialWithPresetOverride = null;
|
||||
public MaterialProperty[] defaultPropertiesWithPresetOverride = null;
|
||||
public List<PersetDynamicData> activePresetDatas = new List<PersetDynamicData>();
|
||||
public int modifiedCount = 0;
|
||||
public Dictionary<string, bool> cachedModifiedProperties = null;
|
||||
public bool forceInit = true;
|
||||
/// <summary>
|
||||
/// Contains Metadata that may be different for each Material.
|
||||
/// </summary>
|
||||
public class PerMaterialData
|
||||
{
|
||||
public bool forceInit = true;
|
||||
public Material material = null;
|
||||
public MaterialProperty[] props = null;
|
||||
public Dictionary<string, PropertyDynamicData> propDynamicDatas = new Dictionary<string, PropertyDynamicData>();
|
||||
|
||||
public PerMaterialData(Shader shader, Material material, MaterialEditor editor, MaterialProperty[] props, PerShaderData perShaderData)
|
||||
{
|
||||
Init(shader, material, editor, props, perShaderData);
|
||||
}
|
||||
public Material defaultMaterialWithPresetOverride = null;
|
||||
public MaterialProperty[] defaultPropertiesWithPresetOverride = null;
|
||||
public List<PresetDynamicData> activePresetDatas = new List<PresetDynamicData>();
|
||||
public int modifiedCount = 0;
|
||||
public Dictionary<string, bool> cachedModifiedProperties = null;
|
||||
|
||||
public void Init(Shader shader, Material material, MaterialEditor editor, MaterialProperty[] props, PerShaderData perShaderData)
|
||||
{
|
||||
forceInit = false;
|
||||
// Performance Monitor
|
||||
public List<string> activeKeywords = null;
|
||||
public List<ShaderPerfData> shaderPerfDatas = null;
|
||||
|
||||
// Reset Datas
|
||||
this.props = props;
|
||||
this.material = material;
|
||||
activePresetDatas.Clear();
|
||||
propDynamicDatas.Clear();
|
||||
modifiedCount = 0;
|
||||
public PerMaterialData(Shader shader, Material material, MaterialEditor editor, MaterialProperty[] props, PerShaderData perShaderData)
|
||||
{
|
||||
Init(shader, material, editor, props, perShaderData);
|
||||
}
|
||||
|
||||
// Get active presets
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var propStaticData = perShaderData.propStaticDatas[prop.name];
|
||||
var activePreset = propStaticData.presetDrawer?.GetActivePreset(prop, propStaticData.propertyPresetAsset);
|
||||
if (activePreset != null
|
||||
// Filter invisible preset properties
|
||||
&& (propStaticData.showIfDatas.Count == 0
|
||||
|| ShowIfDecorator.GetShowIfResultFromMaterial(propStaticData.showIfDatas, this.material)))
|
||||
{
|
||||
activePresetDatas.Add(new PersetDynamicData(activePreset, prop));
|
||||
}
|
||||
}
|
||||
// Signature of the cached default material: which presets and which base material were used to build it.
|
||||
// Compared each Init() to auto-detect preset switching, material.parent changes, etc.
|
||||
private List<LwguiShaderPropertyPreset.Preset> _cachedActivePresets;
|
||||
private Material _cachedDefaultMaterialSource;
|
||||
|
||||
{
|
||||
// Apply presets to default material
|
||||
defaultMaterialWithPresetOverride = UnityEngine.Object.Instantiate(
|
||||
private bool IsDefaultMaterialCacheValid(Material baseMaterial)
|
||||
{
|
||||
if (defaultMaterialWithPresetOverride == null || _cachedActivePresets == null)
|
||||
return false;
|
||||
if (_cachedDefaultMaterialSource != baseMaterial)
|
||||
return false;
|
||||
if (_cachedActivePresets.Count != activePresetDatas.Count)
|
||||
return false;
|
||||
for (int i = 0; i < _cachedActivePresets.Count; i++)
|
||||
{
|
||||
if (_cachedActivePresets[i] != activePresetDatas[i].preset)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void StoreDefaultMaterialCacheSignature(Material baseMaterial)
|
||||
{
|
||||
_cachedDefaultMaterialSource = baseMaterial;
|
||||
if (_cachedActivePresets == null)
|
||||
_cachedActivePresets = new List<LwguiShaderPropertyPreset.Preset>(activePresetDatas.Count);
|
||||
else
|
||||
_cachedActivePresets.Clear();
|
||||
for (int i = 0; i < activePresetDatas.Count; i++)
|
||||
_cachedActivePresets.Add(activePresetDatas[i].preset);
|
||||
}
|
||||
|
||||
public void Init(Shader shader, Material material, MaterialEditor editor, MaterialProperty[] props, PerShaderData perShaderData)
|
||||
{
|
||||
forceInit = false;
|
||||
|
||||
// Reset Datas
|
||||
this.props = props;
|
||||
this.material = material;
|
||||
activePresetDatas.Clear();
|
||||
propDynamicDatas.Clear();
|
||||
modifiedCount = 0;
|
||||
|
||||
// Get active presets
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var propStaticData = perShaderData.propStaticDatas[prop.name];
|
||||
var activePreset = propStaticData.presetDrawer?.GetActivePreset(prop, propStaticData.propertyPresetAsset);
|
||||
if (activePreset != null
|
||||
// Filter invisible preset properties
|
||||
&& (propStaticData.showIfDatas.Count == 0
|
||||
|| ShowIfDecorator.GetShowIfResultFromMaterial(propStaticData.showIfDatas, this.material)))
|
||||
{
|
||||
activePresetDatas.Add(new PresetDynamicData(activePreset, prop));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var baseMaterial =
|
||||
#if UNITY_2022_1_OR_NEWER
|
||||
material.parent
|
||||
? material.parent
|
||||
:
|
||||
material.parent
|
||||
? material.parent
|
||||
:
|
||||
#endif
|
||||
perShaderData.defaultMaterial
|
||||
);
|
||||
perShaderData.defaultMaterial;
|
||||
|
||||
foreach (var activePresetData in activePresetDatas)
|
||||
activePresetData.preset.ApplyToDefaultMaterial(defaultMaterialWithPresetOverride);
|
||||
var cacheWasValid = IsDefaultMaterialCacheValid(baseMaterial);
|
||||
if (!cacheWasValid)
|
||||
{
|
||||
defaultMaterialWithPresetOverride = UnityEngine.Object.Instantiate(baseMaterial);
|
||||
|
||||
defaultPropertiesWithPresetOverride = MaterialEditor.GetMaterialProperties(new[] { defaultMaterialWithPresetOverride });
|
||||
Debug.Assert(defaultPropertiesWithPresetOverride.Length == props.Length);
|
||||
foreach (var activePresetData in activePresetDatas)
|
||||
activePresetData.preset.ApplyToDefaultMaterial(defaultMaterialWithPresetOverride);
|
||||
|
||||
// Init propDynamicDatas
|
||||
for (int i = 0; i < props.Length; i++)
|
||||
{
|
||||
var hasModified = !Helper.PropertyValueEquals(props[i], defaultPropertiesWithPresetOverride[i]);
|
||||
if (hasModified) modifiedCount++;
|
||||
propDynamicDatas.Add(props[i].name, new PropertyDynamicData()
|
||||
{
|
||||
property = props[i],
|
||||
defualtProperty = defaultPropertiesWithPresetOverride[i],
|
||||
hasModified = hasModified
|
||||
});
|
||||
}
|
||||
defaultPropertiesWithPresetOverride = MaterialEditor.GetMaterialProperties(new Object[] { defaultMaterialWithPresetOverride });
|
||||
StoreDefaultMaterialCacheSignature(baseMaterial);
|
||||
}
|
||||
|
||||
// Collect modification
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var propStaticData = perShaderData.propStaticDatas[prop.name];
|
||||
var propDynamicData = propDynamicDatas[prop.name];
|
||||
Debug.Assert(defaultPropertiesWithPresetOverride.Length == props.Length);
|
||||
|
||||
// Extra Prop hasModified
|
||||
foreach (var extraPropName in propStaticData.extraPropNames)
|
||||
propDynamicData.hasModified |= propDynamicDatas[extraPropName].hasModified;
|
||||
// Init propDynamicDatas
|
||||
for (int i = 0; i < props.Length; i++)
|
||||
{
|
||||
var hasModified = !Helper.PropertyValueEquals(props[i], defaultPropertiesWithPresetOverride[i]);
|
||||
if (hasModified) modifiedCount++;
|
||||
propDynamicDatas.Add(props[i].name, new PropertyDynamicData()
|
||||
{
|
||||
property = props[i],
|
||||
defaultProperty = defaultPropertiesWithPresetOverride[i],
|
||||
hasModified = hasModified
|
||||
});
|
||||
}
|
||||
|
||||
// Override parent hasChildrenModified
|
||||
if (propDynamicData.hasModified)
|
||||
{
|
||||
var parentPropData = propStaticData.parent;
|
||||
if (parentPropData != null)
|
||||
{
|
||||
propDynamicDatas[parentPropData.name].hasChildrenModified = true;
|
||||
if (parentPropData.parent != null)
|
||||
propDynamicDatas[parentPropData.parent.name].hasChildrenModified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Collect modification
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var propStaticData = perShaderData.propStaticDatas[prop.name];
|
||||
var propDynamicData = propDynamicDatas[prop.name];
|
||||
|
||||
// Store "Show Modified Props Only" Caches
|
||||
{
|
||||
if (perShaderData.displayModeData.showOnlyModifiedGroups || perShaderData.displayModeData.showOnlyModifiedProperties)
|
||||
{
|
||||
if (cachedModifiedProperties == null)
|
||||
{
|
||||
cachedModifiedProperties = new Dictionary<string, bool>();
|
||||
foreach (var propDynamicDataKWPair in propDynamicDatas)
|
||||
{
|
||||
if (propDynamicDataKWPair.Value.hasModified || propDynamicDataKWPair.Value.hasChildrenModified)
|
||||
cachedModifiedProperties.Add(propDynamicDataKWPair.Key, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
cachedModifiedProperties = null;
|
||||
}
|
||||
// Extra Prop hasModified
|
||||
foreach (var extraPropName in propStaticData.extraPropNames)
|
||||
propDynamicData.hasModified |= propDynamicDatas[extraPropName].hasModified;
|
||||
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var propStaticData = perShaderData.propStaticDatas[prop.name];
|
||||
var propDynamicData = propDynamicDatas[prop.name];
|
||||
// Override parent hasChildrenModified
|
||||
if (propDynamicData.hasModified)
|
||||
{
|
||||
var parentPropData = propStaticData.parent;
|
||||
if (parentPropData != null)
|
||||
{
|
||||
propDynamicDatas[parentPropData.name].hasChildrenModified = true;
|
||||
if (parentPropData.parent != null)
|
||||
propDynamicDatas[parentPropData.parent.name].hasChildrenModified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get default value descriptions
|
||||
propStaticData.baseDrawers?.ForEach(propertyDrawer => propertyDrawer.GetDefaultValueDescription(shader, prop, propDynamicData.defualtProperty, perShaderData, this));
|
||||
if (string.IsNullOrEmpty(propDynamicData.defaultValueDescription))
|
||||
propDynamicData.defaultValueDescription = RevertableHelper.GetPropertyDefaultValueText(propDynamicData.defualtProperty);
|
||||
// Store "Show Modified Props Only" Caches
|
||||
{
|
||||
if (perShaderData.displayModeData.showOnlyModifiedGroups || perShaderData.displayModeData.showOnlyModifiedProperties)
|
||||
{
|
||||
if (cachedModifiedProperties == null)
|
||||
{
|
||||
cachedModifiedProperties = new Dictionary<string, bool>();
|
||||
foreach (var propDynamicDataKWPair in propDynamicDatas)
|
||||
{
|
||||
if (propDynamicDataKWPair.Value.hasModified || propDynamicDataKWPair.Value.hasChildrenModified)
|
||||
cachedModifiedProperties.Add(propDynamicDataKWPair.Key, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
cachedModifiedProperties = null;
|
||||
}
|
||||
|
||||
// Get ShowIf() results
|
||||
ShowIfDecorator.GetShowIfResult(propStaticData, propDynamicData, this);
|
||||
}
|
||||
}
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var propStaticData = perShaderData.propStaticDatas[prop.name];
|
||||
var propDynamicData = propDynamicDatas[prop.name];
|
||||
|
||||
public void Update(Shader shader, Material material, MaterialEditor editor, MaterialProperty[] props, PerShaderData perShaderData)
|
||||
{
|
||||
if (forceInit)
|
||||
{
|
||||
Init(shader, material, editor, props, perShaderData);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var prop in props)
|
||||
{
|
||||
propDynamicDatas[prop.name].property = prop;
|
||||
}
|
||||
}
|
||||
|
||||
// Check animated
|
||||
var renderer = editor.GetRendererForAnimationMode();
|
||||
if (renderer != null)
|
||||
{
|
||||
forceInit = true;
|
||||
foreach (var prop in props)
|
||||
{
|
||||
ReflectionHelper.MaterialAnimationUtility_OverridePropertyColor(prop, renderer, out var color);
|
||||
if (color != Color.white)
|
||||
propDynamicDatas[prop.name].isAnimated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Get default value descriptions
|
||||
propStaticData.baseDrawers?.ForEach(propertyDrawer => propertyDrawer.GetDefaultValueDescription(shader, prop, propDynamicData.defaultProperty, perShaderData, this));
|
||||
if (string.IsNullOrEmpty(propDynamicData.defaultValueDescription))
|
||||
propDynamicData.defaultValueDescription = RevertableHelper.GetPropertyDefaultValueText(propDynamicData.defaultProperty);
|
||||
|
||||
public bool EndChangeCheck(string propName = null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(propName))
|
||||
{
|
||||
GUI.changed |= propDynamicDatas[propName].hasRevertChanged;
|
||||
propDynamicDatas[propName].hasRevertChanged = false;
|
||||
}
|
||||
return EditorGUI.EndChangeCheck();
|
||||
}
|
||||
// Get ShowIf() results
|
||||
ShowIfDecorator.GetShowIfResult(propStaticData, propDynamicData, this);
|
||||
|
||||
// Get ActiveIf() results
|
||||
if (propStaticData.activeIfDatas.Count > 0)
|
||||
propDynamicData.isActive = ShowIfDecorator.GetShowIfResultFromMaterial(propStaticData.activeIfDatas, this.material);
|
||||
}
|
||||
|
||||
public PropertyDynamicData GetPropDynamicData(string propName)
|
||||
{
|
||||
propDynamicDatas.TryGetValue(propName, out var propDynamicData);
|
||||
return propDynamicData;
|
||||
}
|
||||
}
|
||||
// Get Shader Perf Stats
|
||||
if (ToolbarHelper.IsDisplayShaderPerfStatsEnabled(perShaderData.shaderUID))
|
||||
{
|
||||
activeKeywords = ShaderPerfMonitor.GetMaterialAndGlobalAndUserOverrideActiveKeywords(material, perShaderData.shaderUID);
|
||||
shaderPerfDatas = ShaderPerfMonitor.GetShaderVariantPerfDatas(shader, activeKeywords);
|
||||
}
|
||||
}
|
||||
|
||||
public void InvalidateDefaultMaterialCache()
|
||||
{
|
||||
_cachedActivePresets = null;
|
||||
}
|
||||
|
||||
public void Update(Shader shader, Material material, MaterialEditor editor, MaterialProperty[] props, PerShaderData perShaderData)
|
||||
{
|
||||
if (forceInit)
|
||||
{
|
||||
Init(shader, material, editor, props, perShaderData);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var prop in props)
|
||||
{
|
||||
propDynamicDatas[prop.name].property = prop;
|
||||
}
|
||||
}
|
||||
|
||||
// Check animated
|
||||
var renderer = editor.GetRendererForAnimationMode();
|
||||
if (renderer != null)
|
||||
{
|
||||
forceInit = true;
|
||||
foreach (var prop in props)
|
||||
{
|
||||
ReflectionHelper.MaterialAnimationUtility_OverridePropertyColor(prop, renderer, out var color);
|
||||
if (color != Color.white)
|
||||
propDynamicDatas[prop.name].isAnimated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool EndChangeCheck(string propName = null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(propName))
|
||||
{
|
||||
GUI.changed |= propDynamicDatas[propName].hasRevertChanged;
|
||||
propDynamicDatas[propName].hasRevertChanged = false;
|
||||
}
|
||||
return EditorGUI.EndChangeCheck();
|
||||
}
|
||||
|
||||
public PropertyDynamicData GetPropDynamicData(string propName)
|
||||
{
|
||||
propDynamicDatas.TryGetValue(propName, out var propDynamicData);
|
||||
return propDynamicData;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright (c) Jason Ma
|
||||
// Copyright (c) Jason Ma
|
||||
// Per Shader > Per Material > Per Inspector
|
||||
|
||||
using System;
|
||||
@ -9,361 +9,369 @@ using UnityEngine;
|
||||
|
||||
namespace LWGUI
|
||||
{
|
||||
public enum SearchMode
|
||||
{
|
||||
Auto = 0, // Search by group first, and search by property when there are no results
|
||||
Property = 1, // Search by property
|
||||
Group = 2, // Search by group
|
||||
Num = 3
|
||||
}
|
||||
|
||||
public enum SearchMode
|
||||
{
|
||||
Auto = 0, // Search by group first, and search by property when there are no results
|
||||
Property = 1, // Search by property
|
||||
Group = 2, // Search by group
|
||||
Num = 3
|
||||
}
|
||||
public class DisplayModeData
|
||||
{
|
||||
public bool showAllAdvancedProperties;
|
||||
public bool showAllHiddenProperties;
|
||||
public bool showOnlyModifiedProperties;
|
||||
public bool showOnlyModifiedGroups;
|
||||
|
||||
public class DisplayModeData
|
||||
{
|
||||
public bool showAllAdvancedProperties;
|
||||
public bool showAllHiddenProperties;
|
||||
public bool showOnlyModifiedProperties;
|
||||
public bool showOnlyModifiedGroups;
|
||||
public int advancedCount;
|
||||
public int hiddenCount;
|
||||
|
||||
public int advancedCount;
|
||||
public int hiddenCount;
|
||||
public bool IsDefaultDisplayMode()
|
||||
{
|
||||
return !(showAllAdvancedProperties || showAllHiddenProperties || showOnlyModifiedProperties || showOnlyModifiedGroups);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsDefaultDisplayMode() { return !(showAllAdvancedProperties || showAllHiddenProperties || showOnlyModifiedProperties || showOnlyModifiedGroups); }
|
||||
}
|
||||
/// <summary>
|
||||
/// The static metadata of Material Property is only related to Shader.
|
||||
/// </summary>
|
||||
public partial class PropertyStaticData
|
||||
{
|
||||
public string name = string.Empty;
|
||||
public string displayName = string.Empty; // Decoded displayName (Helpbox and Tooltip are encoded in displayName)
|
||||
|
||||
/// <summary>
|
||||
/// The static metadata of Material Property is only related to Shader.
|
||||
/// </summary>
|
||||
public partial class PropertyStaticData
|
||||
{
|
||||
public string name = string.Empty;
|
||||
public string displayName = string.Empty; // Decoded displayName (Helpbox and Tooltip are encoded in displayName)
|
||||
// Structure
|
||||
public string groupName = string.Empty; // [Group(groupName)] / [Sub(groupName)] / [Advanced(groupName)]
|
||||
public bool isMain = false; // [Group]
|
||||
public bool isAdvanced = false; // [Advanced]
|
||||
public bool isAdvancedHeader = false; // the first [Advanced] in the same group
|
||||
public bool isAdvancedHeaderProperty = false;
|
||||
public string advancedHeaderString = string.Empty;
|
||||
public PropertyStaticData parent = null;
|
||||
public List<PropertyStaticData> children = new List<PropertyStaticData>();
|
||||
|
||||
// Structure
|
||||
public string groupName = string.Empty; // [Group(groupName)] / [Sub(groupName)] / [Advanced(groupName)]
|
||||
public bool isMain = false; // [Group]
|
||||
public bool isAdvanced = false; // [Advanced]
|
||||
public bool isAdvancedHeader = false; // the first [Advanced] in the same group
|
||||
public bool isAdvancedHeaderProperty = false;
|
||||
public string advancedHeaderString = string.Empty;
|
||||
public PropertyStaticData parent = null;
|
||||
public List<PropertyStaticData> children = new List<PropertyStaticData>();
|
||||
// Visibility
|
||||
public bool isSearchMatched = true; // Search filter result
|
||||
public bool isExpanding = false; // Children are displayed only when expanded
|
||||
public bool isReadOnly = false; // [ReadOnly]
|
||||
public bool isHidden = false; // [Hidden]
|
||||
public List<ShowIfDecorator.ShowIfData> showIfDatas = new List<ShowIfDecorator.ShowIfData>(); // [ShowIf()]
|
||||
public List<ShowIfDecorator.ShowIfData> activeIfDatas = new List<ShowIfDecorator.ShowIfData>(); // [ActiveIf()]
|
||||
public string conditionalDisplayKeyword = string.Empty; // [Group(groupName_conditionalDisplayKeyword)]
|
||||
|
||||
// Visibility
|
||||
public bool isSearchMatched = true; // Search filter result
|
||||
public bool isExpanding = false; // Children are displayed only when expanded
|
||||
public bool isReadOnly = false; // [ReadOnly]
|
||||
public bool isHidden = false; // [Hidden]
|
||||
public List<ShowIfDecorator.ShowIfData> showIfDatas = new List<ShowIfDecorator.ShowIfData>(); // [ShowIf()]
|
||||
public string conditionalDisplayKeyword = string.Empty; // [Group(groupName_conditionalDisplayKeyword)]
|
||||
// Drawers
|
||||
public IPresetDrawer presetDrawer = null;
|
||||
public List<IBaseDrawer> baseDrawers = null;
|
||||
|
||||
// Drawers
|
||||
public IPresetDrawer presetDrawer = null;
|
||||
public List<IBaseDrawer> baseDrawers = null;
|
||||
// Metadata
|
||||
public List<string> extraPropNames = new List<string>(); // Other Props that have been associated
|
||||
public string helpboxMessages = string.Empty;
|
||||
public string tooltipMessages = string.Empty;
|
||||
public LwguiShaderPropertyPreset propertyPresetAsset = null; // The Referenced Preset Asset
|
||||
|
||||
// Metadata
|
||||
public List<string> extraPropNames = new List<string>(); // Other Props that have been associated
|
||||
public string helpboxMessages = string.Empty;
|
||||
public string tooltipMessages = string.Empty;
|
||||
public LwguiShaderPropertyPreset propertyPresetAsset = null; // The Referenced Preset Asset
|
||||
public void AddExtraProperty(string propName)
|
||||
{
|
||||
if (!extraPropNames.Contains(propName)) extraPropNames.Add(propName);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddExtraProperty(string propName)
|
||||
{
|
||||
if (!extraPropNames.Contains(propName)) extraPropNames.Add(propName);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// All Shader static metadata can be determined after Shader is compiled and will not change.
|
||||
/// </summary>
|
||||
public class PerShaderData
|
||||
{
|
||||
public Dictionary<string, PropertyStaticData> propStaticDatas = new Dictionary<string, PropertyStaticData>();
|
||||
public Shader shader = null;
|
||||
public string shaderUID = string.Empty;
|
||||
public DisplayModeData displayModeData = new DisplayModeData();
|
||||
public SearchMode searchMode = SearchMode.Auto;
|
||||
public string searchString = string.Empty;
|
||||
// public List<string> favoriteproperties = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// All Shader static metadata can be determined after Shader is compiled and will not change.
|
||||
/// </summary>
|
||||
public class PerShaderData
|
||||
{
|
||||
public Dictionary<string, PropertyStaticData> propStaticDatas = new Dictionary<string, PropertyStaticData>();
|
||||
public Shader shader = null;
|
||||
public DisplayModeData displayModeData = new DisplayModeData();
|
||||
public SearchMode searchMode = SearchMode.Auto;
|
||||
public string searchString = string.Empty;
|
||||
// public List<string> favoriteproperties = new List<string>();
|
||||
// UnityEngine.Object may be destroyed when loading new scene, so must manually check null reference
|
||||
private Material _defaultMaterial = null;
|
||||
|
||||
// UnityEngine.Object may be destroyed when loading new scene, so must manually check null reference
|
||||
private Material _defaultMaterial = null;
|
||||
public Material defaultMaterial
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_defaultMaterial && shader) _defaultMaterial = new Material(shader);
|
||||
return _defaultMaterial;
|
||||
}
|
||||
}
|
||||
|
||||
public Material defaultMaterial
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_defaultMaterial && shader) _defaultMaterial = new Material(shader);
|
||||
return _defaultMaterial;
|
||||
}
|
||||
}
|
||||
public PerShaderData(Shader shader, MaterialProperty[] props)
|
||||
{
|
||||
this.shader = shader;
|
||||
|
||||
if (AssetDatabase.Contains(shader))
|
||||
shaderUID = AssetDatabase.GetAssetPath(shader).Replace('/', '_').Replace('\\', '_');
|
||||
else
|
||||
shaderUID = shader.GetHashCode().ToString();
|
||||
|
||||
public PerShaderData(Shader shader, MaterialProperty[] props)
|
||||
{
|
||||
this.shader = shader;
|
||||
// Get Property Static Data
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var propStaticData = new PropertyStaticData() { name = prop.name };
|
||||
propStaticDatas[prop.name] = propStaticData;
|
||||
|
||||
// Get Property Static Data
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var propStaticData = new PropertyStaticData() { name = prop.name };
|
||||
propStaticDatas[prop.name] = propStaticData;
|
||||
// Get Drawers and Build Drawer StaticMetaData
|
||||
bool hasDecodedStaticMetaData = false;
|
||||
{
|
||||
var drawer = ReflectionHelper.GetPropertyDrawer(shader, prop, out var decoratorDrawers);
|
||||
|
||||
// Get Drawers and Build Drawer StaticMetaData
|
||||
bool hasDecodedStaticMetaData = false;
|
||||
{
|
||||
var drawer = ReflectionHelper.GetPropertyDrawer(shader, prop, out var decoratorDrawers);
|
||||
if (drawer is IPresetDrawer)
|
||||
propStaticData.presetDrawer = drawer as IPresetDrawer;
|
||||
|
||||
if (drawer is IPresetDrawer)
|
||||
propStaticData.presetDrawer = drawer as IPresetDrawer;
|
||||
var baseDrawer = drawer as IBaseDrawer;
|
||||
if (baseDrawer != null)
|
||||
{
|
||||
propStaticData.baseDrawers = new List<IBaseDrawer>() { baseDrawer };
|
||||
baseDrawer.BuildStaticMetaData(shader, prop, props, propStaticData);
|
||||
hasDecodedStaticMetaData = true;
|
||||
}
|
||||
|
||||
var baseDrawer = drawer as IBaseDrawer;
|
||||
if (baseDrawer != null)
|
||||
{
|
||||
propStaticData.baseDrawers = new List<IBaseDrawer>() { baseDrawer };
|
||||
baseDrawer.BuildStaticMetaData(shader, prop, props, propStaticData);
|
||||
hasDecodedStaticMetaData = true;
|
||||
}
|
||||
decoratorDrawers?.ForEach(decoratorDrawer =>
|
||||
{
|
||||
baseDrawer = decoratorDrawer as IBaseDrawer;
|
||||
if (baseDrawer != null)
|
||||
{
|
||||
if (propStaticData.baseDrawers == null)
|
||||
propStaticData.baseDrawers = new List<IBaseDrawer>() { baseDrawer };
|
||||
else
|
||||
propStaticData.baseDrawers.Add(baseDrawer);
|
||||
|
||||
decoratorDrawers?.ForEach(decoratorDrawer =>
|
||||
{
|
||||
baseDrawer = decoratorDrawer as IBaseDrawer;
|
||||
if (baseDrawer != null)
|
||||
{
|
||||
if (propStaticData.baseDrawers == null)
|
||||
propStaticData.baseDrawers = new List<IBaseDrawer>() { baseDrawer };
|
||||
else
|
||||
propStaticData.baseDrawers.Add(baseDrawer);
|
||||
baseDrawer.BuildStaticMetaData(shader, prop, props, propStaticData);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
baseDrawer.BuildStaticMetaData(shader, prop, props, propStaticData);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!hasDecodedStaticMetaData)
|
||||
DecodeMetaDataFromDisplayName(prop, propStaticData);
|
||||
}
|
||||
|
||||
if (!hasDecodedStaticMetaData)
|
||||
DecodeMetaDataFromDisplayName(prop, propStaticData);
|
||||
}
|
||||
// Check Data
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var propStaticData = propStaticDatas[prop.name];
|
||||
propStaticData.extraPropNames.RemoveAll((extraPropName =>
|
||||
string.IsNullOrEmpty(extraPropName) || !propStaticDatas.ContainsKey(extraPropName)));
|
||||
}
|
||||
|
||||
// Check Data
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var propStaticData = propStaticDatas[prop.name];
|
||||
propStaticData.extraPropNames.RemoveAll((extraPropName =>
|
||||
string.IsNullOrEmpty(extraPropName) || !propStaticDatas.ContainsKey(extraPropName)));
|
||||
}
|
||||
// Build Property Structure
|
||||
{
|
||||
var groupToMainPropertyDic = new Dictionary<string, MaterialProperty>();
|
||||
|
||||
// Build Property Structure
|
||||
{
|
||||
var groupToMainPropertyDic = new Dictionary<string, MaterialProperty>();
|
||||
// Collection Groups
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var propData = propStaticDatas[prop.name];
|
||||
if (propData.isMain
|
||||
&& !string.IsNullOrEmpty(propData.groupName)
|
||||
&& !groupToMainPropertyDic.ContainsKey(propData.groupName))
|
||||
groupToMainPropertyDic.Add(propData.groupName, prop);
|
||||
}
|
||||
|
||||
// Collection Groups
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var propData = propStaticDatas[prop.name];
|
||||
if (propData.isMain
|
||||
&& !string.IsNullOrEmpty(propData.groupName)
|
||||
&& !groupToMainPropertyDic.ContainsKey(propData.groupName))
|
||||
groupToMainPropertyDic.Add(propData.groupName, prop);
|
||||
}
|
||||
// Register SubProps
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var propData = propStaticDatas[prop.name];
|
||||
if (!propData.isMain
|
||||
&& !string.IsNullOrEmpty(propData.groupName))
|
||||
{
|
||||
foreach (var groupName in groupToMainPropertyDic.Keys)
|
||||
{
|
||||
if (propData.groupName.StartsWith(groupName))
|
||||
{
|
||||
// Update Structure
|
||||
var mainProp = groupToMainPropertyDic[groupName];
|
||||
propData.parent = propStaticDatas[mainProp.name];
|
||||
propStaticDatas[mainProp.name].children.Add(propData);
|
||||
|
||||
// Register SubProps
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var propData = propStaticDatas[prop.name];
|
||||
if (!propData.isMain
|
||||
&& !string.IsNullOrEmpty(propData.groupName))
|
||||
{
|
||||
foreach (var groupName in groupToMainPropertyDic.Keys)
|
||||
{
|
||||
if (propData.groupName.StartsWith(groupName))
|
||||
{
|
||||
// Update Structure
|
||||
var mainProp = groupToMainPropertyDic[groupName];
|
||||
propData.parent = propStaticDatas[mainProp.name];
|
||||
propStaticDatas[mainProp.name].children.Add(propData);
|
||||
// Split groupName and conditional display keyword
|
||||
if (propData.groupName.Length > groupName.Length)
|
||||
{
|
||||
propData.conditionalDisplayKeyword =
|
||||
propData.groupName.Substring(groupName.Length, propData.groupName.Length - groupName.Length).ToUpper();
|
||||
propData.groupName = groupName;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Split groupName and conditional display keyword
|
||||
if (propData.groupName.Length > groupName.Length)
|
||||
{
|
||||
propData.conditionalDisplayKeyword =
|
||||
propData.groupName.Substring(groupName.Length, propData.groupName.Length - groupName.Length).ToUpper();
|
||||
propData.groupName = groupName;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Build Display Mode Data
|
||||
{
|
||||
PropertyStaticData lastPropData = null;
|
||||
PropertyStaticData lastHeaderPropData = null;
|
||||
for (int i = 0; i < props.Length; i++)
|
||||
{
|
||||
var prop = props[i];
|
||||
var propStaticData = propStaticDatas[prop.name];
|
||||
|
||||
// Build Display Mode Data
|
||||
{
|
||||
PropertyStaticData lastPropData = null;
|
||||
PropertyStaticData lastHeaderPropData = null;
|
||||
for (int i = 0; i < props.Length; i++)
|
||||
{
|
||||
var prop = props[i];
|
||||
var propStaticData = propStaticDatas[prop.name];
|
||||
// Counting
|
||||
if (propStaticData.isHidden
|
||||
|| (propStaticData.parent != null
|
||||
&& (propStaticData.parent.isHidden
|
||||
|| (propStaticData.parent.parent != null && propStaticData.parent.parent.isHidden))))
|
||||
displayModeData.hiddenCount++;
|
||||
if (propStaticData.isAdvanced
|
||||
|| (propStaticData.parent != null
|
||||
&& (propStaticData.parent.isAdvanced
|
||||
|| (propStaticData.parent.parent != null && propStaticData.parent.parent.isAdvanced))))
|
||||
displayModeData.advancedCount++;
|
||||
|
||||
// Counting
|
||||
if (propStaticData.isHidden
|
||||
|| (propStaticData.parent != null
|
||||
&& (propStaticData.parent.isHidden
|
||||
|| (propStaticData.parent.parent != null && propStaticData.parent.parent.isHidden))))
|
||||
displayModeData.hiddenCount++;
|
||||
if (propStaticData.isAdvanced
|
||||
|| (propStaticData.parent != null
|
||||
&& (propStaticData.parent.isAdvanced
|
||||
|| (propStaticData.parent.parent != null && propStaticData.parent.parent.isAdvanced))))
|
||||
displayModeData.advancedCount++;
|
||||
// Build Advanced Structure
|
||||
if (propStaticData.isAdvanced)
|
||||
{
|
||||
// If it is the first prop in a Advanced Block, set to Header
|
||||
if (lastPropData == null
|
||||
|| !lastPropData.isAdvanced
|
||||
|| propStaticData.isAdvancedHeaderProperty
|
||||
|| (!string.IsNullOrEmpty(propStaticData.advancedHeaderString)
|
||||
&& propStaticData.advancedHeaderString != lastPropData.advancedHeaderString))
|
||||
{
|
||||
propStaticData.isAdvancedHeader = true;
|
||||
lastHeaderPropData = propStaticData;
|
||||
}
|
||||
// Else set to child
|
||||
else
|
||||
{
|
||||
propStaticData.parent = lastHeaderPropData;
|
||||
lastHeaderPropData.children.Add(propStaticData);
|
||||
}
|
||||
}
|
||||
|
||||
// Build Advanced Structure
|
||||
if (propStaticData.isAdvanced)
|
||||
{
|
||||
// If it is the first prop in a Advanced Block, set to Header
|
||||
if (lastPropData == null
|
||||
|| !lastPropData.isAdvanced
|
||||
|| propStaticData.isAdvancedHeaderProperty
|
||||
|| (!string.IsNullOrEmpty(propStaticData.advancedHeaderString)
|
||||
&& propStaticData.advancedHeaderString != lastPropData.advancedHeaderString))
|
||||
{
|
||||
propStaticData.isAdvancedHeader = true;
|
||||
lastHeaderPropData = propStaticData;
|
||||
}
|
||||
// Else set to child
|
||||
else
|
||||
{
|
||||
propStaticData.parent = lastHeaderPropData;
|
||||
lastHeaderPropData.children.Add(propStaticData);
|
||||
}
|
||||
}
|
||||
lastPropData = propStaticData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastPropData = propStaticData;
|
||||
}
|
||||
}
|
||||
}
|
||||
public PropertyStaticData GetPropStaticData(string propName)
|
||||
{
|
||||
propStaticDatas.TryGetValue(propName, out var propStaticData);
|
||||
return propStaticData;
|
||||
}
|
||||
|
||||
public PropertyStaticData GetPropStaticData(string propName)
|
||||
{
|
||||
propStaticDatas.TryGetValue(propName, out var propStaticData);
|
||||
return propStaticData;
|
||||
}
|
||||
private static readonly string _tooltipSplitter = "#";
|
||||
|
||||
private static readonly string _tooltipSplitter = "#";
|
||||
private static readonly string _helpboxSplitter = "%";
|
||||
|
||||
private static readonly string _helpboxSplitter = "%";
|
||||
public static void DecodeMetaDataFromDisplayName(MaterialProperty prop, PropertyStaticData propStaticData)
|
||||
{
|
||||
var tooltips = prop.displayName.Split(new String[] { _tooltipSplitter }, StringSplitOptions.None);
|
||||
if (tooltips.Length > 1)
|
||||
{
|
||||
for (int i = 1; i <= tooltips.Length - 1; i++)
|
||||
{
|
||||
var str = tooltips[i];
|
||||
var helpboxIndex = tooltips[i].IndexOf(_helpboxSplitter, StringComparison.Ordinal);
|
||||
if (helpboxIndex > 0)
|
||||
str = tooltips[i].Substring(0, helpboxIndex);
|
||||
propStaticData.tooltipMessages += str + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
public static void DecodeMetaDataFromDisplayName(MaterialProperty prop, PropertyStaticData propStaticData)
|
||||
{
|
||||
var tooltips = prop.displayName.Split(new String[] { _tooltipSplitter }, StringSplitOptions.None);
|
||||
if (tooltips.Length > 1)
|
||||
{
|
||||
for (int i = 1; i <= tooltips.Length - 1; i++)
|
||||
{
|
||||
var str = tooltips[i];
|
||||
var helpboxIndex = tooltips[i].IndexOf(_helpboxSplitter, StringComparison.Ordinal);
|
||||
if (helpboxIndex > 0)
|
||||
str = tooltips[i].Substring(0, helpboxIndex);
|
||||
propStaticData.tooltipMessages += str + "\n";
|
||||
}
|
||||
}
|
||||
var helpboxes = prop.displayName.Split(new String[] { _helpboxSplitter }, StringSplitOptions.None);
|
||||
if (helpboxes.Length > 1)
|
||||
{
|
||||
for (int i = 1; i <= helpboxes.Length - 1; i++)
|
||||
{
|
||||
var str = helpboxes[i];
|
||||
var tooltipIndex = helpboxes[i].IndexOf(_tooltipSplitter, StringComparison.Ordinal);
|
||||
if (tooltipIndex > 0)
|
||||
str = tooltips[i].Substring(0, tooltipIndex);
|
||||
propStaticData.helpboxMessages += str + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
var helpboxes = prop.displayName.Split(new String[] { _helpboxSplitter }, StringSplitOptions.None);
|
||||
if (helpboxes.Length > 1)
|
||||
{
|
||||
for (int i = 1; i <= helpboxes.Length - 1; i++)
|
||||
{
|
||||
var str = helpboxes[i];
|
||||
var tooltipIndex = helpboxes[i].IndexOf(_tooltipSplitter, StringComparison.Ordinal);
|
||||
if (tooltipIndex > 0)
|
||||
str = tooltips[i].Substring(0, tooltipIndex);
|
||||
propStaticData.helpboxMessages += str + "\n";
|
||||
}
|
||||
}
|
||||
if (propStaticData.helpboxMessages.EndsWith("\n"))
|
||||
propStaticData.helpboxMessages = propStaticData.helpboxMessages.Substring(0, propStaticData.helpboxMessages.Length - 1);
|
||||
|
||||
if (propStaticData.helpboxMessages.EndsWith("\n"))
|
||||
propStaticData.helpboxMessages = propStaticData.helpboxMessages.Substring(0, propStaticData.helpboxMessages.Length - 1);
|
||||
propStaticData.displayName = prop.displayName.Split(new String[] { _tooltipSplitter, _helpboxSplitter }, StringSplitOptions.None)[0];
|
||||
}
|
||||
|
||||
propStaticData.displayName = prop.displayName.Split(new String[] { _tooltipSplitter, _helpboxSplitter }, StringSplitOptions.None)[0];
|
||||
}
|
||||
public void UpdateSearchFilter()
|
||||
{
|
||||
var isSearchStringEmpty = string.IsNullOrEmpty(searchString);
|
||||
var searchStringLower = searchString.ToLower();
|
||||
var searchKeywords = searchStringLower.Split(' ', ',', ';', '|', ',', ';'); // Some possible separators
|
||||
|
||||
public void UpdateSearchFilter()
|
||||
{
|
||||
var isSearchStringEmpty = string.IsNullOrEmpty(searchString);
|
||||
var searchStringLower = searchString.ToLower();
|
||||
var searchKeywords = searchStringLower.Split(' ', ',', ';', '|', ',', ';'); // Some possible separators
|
||||
// The First Search
|
||||
foreach (var propStaticDataKWPair in propStaticDatas)
|
||||
{
|
||||
propStaticDataKWPair.Value.isSearchMatched = isSearchStringEmpty
|
||||
? true
|
||||
: IsWholeWordMatch(propStaticDataKWPair.Value.displayName, propStaticDataKWPair.Value.name, searchKeywords);
|
||||
}
|
||||
|
||||
// The First Search
|
||||
foreach (var propStaticDataKWPair in propStaticDatas)
|
||||
{
|
||||
propStaticDataKWPair.Value.isSearchMatched = isSearchStringEmpty
|
||||
? true
|
||||
: IsWholeWordMatch(propStaticDataKWPair.Value.displayName, propStaticDataKWPair.Value.name, searchKeywords);
|
||||
}
|
||||
// Further adjust visibility
|
||||
if (!isSearchStringEmpty)
|
||||
{
|
||||
var searchModeTemp = searchMode;
|
||||
// Auto: search by group first, and search by property when there are no results
|
||||
if (searchModeTemp == SearchMode.Auto)
|
||||
{
|
||||
// if has no group
|
||||
if (!propStaticDatas.Any((propStaticDataKWPair => propStaticDataKWPair.Value.isSearchMatched && propStaticDataKWPair.Value.isMain)))
|
||||
searchModeTemp = SearchMode.Property;
|
||||
else
|
||||
searchModeTemp = SearchMode.Group;
|
||||
}
|
||||
|
||||
// Further adjust visibility
|
||||
if (!isSearchStringEmpty)
|
||||
{
|
||||
var searchModeTemp = searchMode;
|
||||
// Auto: search by group first, and search by property when there are no results
|
||||
if (searchModeTemp == SearchMode.Auto)
|
||||
{
|
||||
// if has no group
|
||||
if (!propStaticDatas.Any((propStaticDataKWPair => propStaticDataKWPair.Value.isSearchMatched && propStaticDataKWPair.Value.isMain)))
|
||||
searchModeTemp = SearchMode.Property;
|
||||
else
|
||||
searchModeTemp = SearchMode.Group;
|
||||
}
|
||||
// search by property
|
||||
if (searchModeTemp == SearchMode.Property)
|
||||
{
|
||||
// when a SubProp is displayed, the MainProp is also displayed
|
||||
foreach (var propStaticDataKWPair in propStaticDatas)
|
||||
{
|
||||
var propStaticData = propStaticDataKWPair.Value;
|
||||
if (propStaticData.isMain
|
||||
&& propStaticData.children.Any((childPropStaticData => propStaticDatas[childPropStaticData.name].isSearchMatched)))
|
||||
propStaticDataKWPair.Value.isSearchMatched = true;
|
||||
}
|
||||
}
|
||||
// search by group
|
||||
else if (searchModeTemp == SearchMode.Group)
|
||||
{
|
||||
// when search by group, all SubProps should display with MainProp
|
||||
foreach (var propStaticDataKWPair in propStaticDatas)
|
||||
{
|
||||
var propStaticData = propStaticDataKWPair.Value;
|
||||
if (propStaticData.isMain)
|
||||
foreach (var childPropStaticData in propStaticData.children)
|
||||
propStaticDatas[childPropStaticData.name].isSearchMatched = propStaticData.isSearchMatched;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// search by property
|
||||
if (searchModeTemp == SearchMode.Property)
|
||||
{
|
||||
// when a SubProp is displayed, the MainProp is also displayed
|
||||
foreach (var propStaticDataKWPair in propStaticDatas)
|
||||
{
|
||||
var propStaticData = propStaticDataKWPair.Value;
|
||||
if (propStaticData.isMain
|
||||
&& propStaticData.children.Any((childPropStaticData => propStaticDatas[childPropStaticData.name].isSearchMatched)))
|
||||
propStaticDataKWPair.Value.isSearchMatched = true;
|
||||
}
|
||||
}
|
||||
// search by group
|
||||
else if (searchModeTemp == SearchMode.Group)
|
||||
{
|
||||
// when search by group, all SubProps should display with MainProp
|
||||
foreach (var propStaticDataKWPair in propStaticDatas)
|
||||
{
|
||||
var propStaticData = propStaticDataKWPair.Value;
|
||||
if (propStaticData.isMain)
|
||||
foreach (var childPropStaticData in propStaticData.children)
|
||||
propStaticDatas[childPropStaticData.name].isSearchMatched = propStaticData.isSearchMatched;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private static bool IsWholeWordMatch(string displayName, string propertyName, string[] searchingKeywords)
|
||||
{
|
||||
bool contains = true;
|
||||
displayName = displayName.ToLower();
|
||||
var name = propertyName.ToLower();
|
||||
|
||||
private static bool IsWholeWordMatch(string displayName, string propertyName, string[] searchingKeywords)
|
||||
{
|
||||
bool contains = true;
|
||||
displayName = displayName.ToLower();
|
||||
var name = propertyName.ToLower();
|
||||
foreach (var keyword in searchingKeywords)
|
||||
{
|
||||
var isMatch = false;
|
||||
isMatch |= displayName.Contains(keyword);
|
||||
isMatch |= name.Contains(keyword);
|
||||
contains &= isMatch;
|
||||
}
|
||||
return contains;
|
||||
}
|
||||
|
||||
foreach (var keyword in searchingKeywords)
|
||||
{
|
||||
var isMatch = false;
|
||||
isMatch |= displayName.Contains(keyword);
|
||||
isMatch |= name.Contains(keyword);
|
||||
contains &= isMatch;
|
||||
}
|
||||
return contains;
|
||||
}
|
||||
|
||||
public void ToggleShowAllAdvancedProperties()
|
||||
{
|
||||
foreach (var propStaticDataKWPair in propStaticDatas)
|
||||
{
|
||||
if (propStaticDataKWPair.Value.isAdvancedHeader)
|
||||
propStaticDataKWPair.Value.isExpanding = displayModeData.showAllAdvancedProperties;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public void ToggleShowAllAdvancedProperties()
|
||||
{
|
||||
foreach (var propStaticDataKWPair in propStaticDatas)
|
||||
{
|
||||
if (propStaticDataKWPair.Value.isAdvancedHeader)
|
||||
propStaticDataKWPair.Value.isExpanding = displayModeData.showAllAdvancedProperties;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 699d09d8caac43769acdfc186d37b21b
|
||||
timeCreated: 1760615698
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c48ecf063064446097c4da1c6ee1c085
|
||||
timeCreated: 1760615805
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f2edf52a23ac4bedb7dd9866bae7383a
|
||||
timeCreated: 1760616152
|
||||
@ -0,0 +1,460 @@
|
||||
// Copyright (c) Jason Ma
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Rendering;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using LWGUI.PerformanceMonitor;
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
namespace LWGUI.PerformanceMonitor.ShaderCompiler
|
||||
{
|
||||
public class ShaderCompilerDefaultFxc : IShaderCompiler
|
||||
{
|
||||
// Per-compiler stats structure moved here so FXC owns its output data shape.
|
||||
public struct ShaderPerfStats
|
||||
{
|
||||
public float estimatedCost; // Estimated relative performance cost based on experience, not precise results.
|
||||
public int sampleCount;
|
||||
public int samplerCount;
|
||||
public int registerCount;
|
||||
public int interpolatorChannelCount;
|
||||
|
||||
public bool isValid;
|
||||
}
|
||||
|
||||
private static ShaderCompilerDefaultFxc _instance;
|
||||
public static ShaderCompilerDefaultFxc instance => _instance ??= new ShaderCompilerDefaultFxc();
|
||||
|
||||
#if UNITY_STANDALONE_WIN
|
||||
public static bool isSupportCurrentPlatform => true;
|
||||
#else
|
||||
public static bool isSupportCurrentPlatform => false;
|
||||
#endif
|
||||
|
||||
public static int priority => 0;
|
||||
|
||||
public string compilerName => "Default Fxc";
|
||||
|
||||
public ShaderCompilerPlatform api { get; set; } = ShaderCompilerPlatform.D3D;
|
||||
public BuildTarget target { get; set; } = BuildTarget.StandaloneWindows64;
|
||||
public GraphicsTier tier { get; set; } = (GraphicsTier)(-1);
|
||||
|
||||
public string GetCompiledShaderPath(ShaderPerfData shaderPerfData, string compiledShaderDirectory, string shaderTypeName)
|
||||
=> Path.Combine(compiledShaderDirectory, $"Fxc_{api}_{target}_{shaderTypeName}.txt");
|
||||
|
||||
public string GetCompiledDxbcPath(ShaderPerfData shaderPerfData)
|
||||
=> Path.Combine(shaderPerfData.compiledShaderDirectory, $"Fxc_{api}_{target}_{shaderPerfData.shaderTypeName}.dxbc");
|
||||
|
||||
public bool CompilePass(ShaderPerfData shaderPerfData, ShaderData.Pass pass, ShaderType shaderType, string[] keywords,
|
||||
out string compiledShader)
|
||||
{
|
||||
compiledShader = string.Empty;
|
||||
|
||||
if (shaderPerfData == null || pass == null || keywords == null)
|
||||
return false;
|
||||
|
||||
var compileInfo = pass.CompileVariant(shaderType, keywords, api, target, tier, true);
|
||||
if (!compileInfo.Success)
|
||||
return false;
|
||||
|
||||
// Write DXBC
|
||||
var dxbcPath = GetCompiledDxbcPath(shaderPerfData);
|
||||
IOHelper.WriteBinaryFile(dxbcPath, compileInfo.ShaderData);
|
||||
|
||||
// Disassemble With fxc.exe
|
||||
return IOHelper.RunProcess(_fxcAbsPath, $"/dumpbin \"{dxbcPath}\"", out compiledShader);
|
||||
}
|
||||
|
||||
public object AnalyzeShaderPerformance(ShaderPerfData shaderPerfData, string compiledShader)
|
||||
=> ParseAsmStats(compiledShader);
|
||||
|
||||
public void DrawShaderPerformanceStatsHeader(LWGUIMetaDatas metaDatas)
|
||||
{
|
||||
EditorGUILayout.LabelField(" ", " Cost Samples Registers");
|
||||
}
|
||||
|
||||
public void DrawShaderPerformanceStatsLine(LWGUIMetaDatas metaDatas, ShaderPerfData shaderPerfData)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
var statsObj = shaderPerfData.stats;
|
||||
if (statsObj is ShaderPerfStats { isValid: true } stats)
|
||||
{
|
||||
var statsStr = $"{stats.estimatedCost,7:0.0} {stats.sampleCount,7:0} {stats.registerCount,8:0}";
|
||||
EditorGUILayout.LabelField($"{shaderPerfData.passName} | {shaderPerfData.shaderTypeName}", statsStr, GUIStyles.label_monospace);
|
||||
|
||||
ToolbarHelper.DrawShaderPerformanceStatsLineButtons(shaderPerfData);
|
||||
}
|
||||
else
|
||||
{
|
||||
var status = shaderPerfData.isCompiledSuccessful ? "ANALYSIS FAILED" : "COMPILATION FAILED";
|
||||
EditorGUILayout.LabelField($"{shaderPerfData.passName} | {shaderPerfData.shaderTypeName}", status);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
|
||||
private static string _cachedFxcPath;
|
||||
|
||||
private static string _fxcAbsPath
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(_cachedFxcPath))
|
||||
_cachedFxcPath = IOHelper.GetAbsPath(AssetDatabase.GUIDToAssetPath("994434336edc8a8469c9afcbb92c5936"));
|
||||
if (string.IsNullOrEmpty(_cachedFxcPath) || !File.Exists(_cachedFxcPath))
|
||||
Debug.LogError("LWGUI: Can not find fxc.exe!");
|
||||
return _cachedFxcPath;
|
||||
}
|
||||
}
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/shader-model-5-assembly--directx-hlsl-
|
||||
private static readonly Dictionary<string, float> _opcodeWeight = new()
|
||||
{
|
||||
// ALU
|
||||
{ "add", 1.0f },
|
||||
{ "and", 1.0f },
|
||||
{ "bfi", 1.0f },
|
||||
{ "bfrev", 1.0f },
|
||||
{ "countbits", 1.0f },
|
||||
{ "dadd", 4.0f },
|
||||
{ "ddiv", 16.0f },
|
||||
{ "deq", 4.0f },
|
||||
{ "deriv_rtx_coarse", 1.0f },
|
||||
{ "deriv_rtx_fine", 1.0f },
|
||||
{ "deriv_rty_coarse", 1.0f },
|
||||
{ "deriv_rty_fine", 1.0f },
|
||||
{ "dfma", 4.0f },
|
||||
{ "dge", 4.0f },
|
||||
{ "div", 8.0f },
|
||||
{ "dlt", 4.0f },
|
||||
{ "dmax", 4.0f },
|
||||
{ "dmin", 4.0f },
|
||||
{ "dmov", 1.0f },
|
||||
{ "dmovc", 1.0f },
|
||||
{ "dmul", 4.0f },
|
||||
{ "dne", 4.0f },
|
||||
{ "dp2", 2.0f },
|
||||
{ "dp3", 3.0f },
|
||||
{ "dp4", 4.0f },
|
||||
{ "drcp", 8.0f },
|
||||
{ "eq", 1.0f },
|
||||
{ "exp", 4.0f },
|
||||
{ "f16tof32", 1.0f },
|
||||
{ "f32tof16", 1.0f },
|
||||
{ "firstbit", 1.0f },
|
||||
{ "frc", 1.0f },
|
||||
{ "ftod", 2.0f },
|
||||
{ "ftoi", 1.0f },
|
||||
{ "ftou", 1.0f },
|
||||
{ "ge", 1.0f },
|
||||
{ "iadd", 1.0f },
|
||||
{ "ibfe", 1.0f },
|
||||
{ "ieq", 1.0f },
|
||||
{ "ige", 1.0f },
|
||||
{ "ilt", 1.0f },
|
||||
{ "imad", 1.0f },
|
||||
{ "imin", 1.0f },
|
||||
{ "imul", 1.0f },
|
||||
{ "ine", 1.0f },
|
||||
{ "ineg", 1.0f },
|
||||
{ "ishl", 1.0f },
|
||||
{ "ishr", 1.0f },
|
||||
{ "itof", 1.0f },
|
||||
{ "log", 4.0f },
|
||||
{ "lt", 1.0f },
|
||||
{ "mad", 1.0f },
|
||||
{ "max", 1.0f },
|
||||
{ "min", 1.0f },
|
||||
{ "mov", 1.0f },
|
||||
{ "movc", 1.0f },
|
||||
{ "mul", 1.0f },
|
||||
{ "ne", 1.0f },
|
||||
{ "not", 1.0f },
|
||||
{ "or", 1.0f },
|
||||
{ "rcp", 4.0f },
|
||||
{ "round_ne", 1.0f },
|
||||
{ "round_ni", 1.0f },
|
||||
{ "round_pi", 1.0f },
|
||||
{ "round_z", 1.0f },
|
||||
{ "rsq", 4.0f },
|
||||
{ "sincos", 4.0f },
|
||||
{ "sqrt", 4.0f },
|
||||
{ "swapc", 1.0f },
|
||||
{ "uaddc", 1.0f },
|
||||
{ "ubfe", 1.0f },
|
||||
{ "udiv", 8.0f },
|
||||
{ "uge", 1.0f },
|
||||
{ "ult", 1.0f },
|
||||
{ "umad", 1.0f },
|
||||
{ "umax", 1.0f },
|
||||
{ "umin", 1.0f },
|
||||
{ "umul", 1.0f },
|
||||
{ "ushr", 1.0f },
|
||||
{ "usubb", 1.0f },
|
||||
{ "utof", 1.0f },
|
||||
{ "xor", 1.0f },
|
||||
|
||||
// Non-ALU (ignored)
|
||||
{ "atomic_and", 0f },
|
||||
{ "atomic_cmp_store", 0f },
|
||||
{ "atomic_iadd", 0f },
|
||||
{ "atomic_imax", 0f },
|
||||
{ "atomic_imin", 0f },
|
||||
{ "atomic_or", 0f },
|
||||
{ "atomic_umax", 0f },
|
||||
{ "atomic_umin", 0f },
|
||||
{ "atomic_xor", 0f },
|
||||
{ "break", 0f },
|
||||
{ "breakc", 0f },
|
||||
{ "bufinfo", 0f },
|
||||
{ "call", 0f },
|
||||
{ "callc", 0f },
|
||||
{ "case", 0f },
|
||||
{ "continue", 0f },
|
||||
{ "continuec", 0f },
|
||||
{ "cut", 0f },
|
||||
{ "cut_stream", 0f },
|
||||
{ "dcl_constantbuffer", 0f },
|
||||
{ "dcl_function_body", 0f },
|
||||
{ "dcl_function_table", 0f },
|
||||
{ "dcl_globalflags", 0f },
|
||||
{ "dcl_hs_fork_phase_instance_count", 0f },
|
||||
{ "dcl_hs_join_phase_instance_count", 0f },
|
||||
{ "dcl_hs_max_tessfactor", 0f },
|
||||
{ "dcl_immediateconstantbuffer", 0f },
|
||||
{ "dcl_indexabletemp", 0f },
|
||||
{ "dcl_indexrange", 0f },
|
||||
{ "dcl_input", 0f },
|
||||
{ "dcl_input vforkinstanceid", 0f },
|
||||
{ "dcl_input vgsinstanceid", 0f },
|
||||
{ "dcl_input vjoininstanceid", 0f },
|
||||
{ "dcl_input voutputcontrolpointid", 0f },
|
||||
{ "dcl_input vprim", 0f },
|
||||
{ "dcl_input vthread", 0f },
|
||||
{ "dcl_input_control_point_count", 0f },
|
||||
{ "dcl_input_sv", 0f },
|
||||
{ "dcl_inputprimitive", 0f },
|
||||
{ "dcl_interface", 0f },
|
||||
{ "dcl_interface_dynamicindexed", 0f },
|
||||
{ "dcl_maxoutputvertexcount", 0f },
|
||||
{ "dcl_output", 0f },
|
||||
{ "dcl_output odepth", 0f },
|
||||
{ "dcl_output omask", 0f },
|
||||
{ "dcl_output_control_point_count", 0f },
|
||||
{ "dcl_output_sgv", 0f },
|
||||
{ "dcl_output_siv", 0f },
|
||||
{ "dcl_outputtopology", 0f },
|
||||
{ "dcl_resource", 0f },
|
||||
{ "dcl_resource raw", 0f },
|
||||
{ "dcl_resource structured", 0f },
|
||||
{ "dcl_sampler", 0f },
|
||||
{ "dcl_stream", 0f },
|
||||
{ "dcl_temps", 0f },
|
||||
{ "dcl_tessellator_domain", 0f },
|
||||
{ "dcl_tessellator_output_primitive", 0f },
|
||||
{ "dcl_tessellator_partitioning", 0f },
|
||||
{ "dcl_tgsm_raw", 0f },
|
||||
{ "dcl_tgsm_structured", 0f },
|
||||
{ "dcl_thread_group", 0f },
|
||||
{ "dcl_uav_raw", 0f },
|
||||
{ "dcl_uav_structured", 0f },
|
||||
{ "dcl_uav_typed", 0f },
|
||||
{ "default", 0f },
|
||||
{ "discard", 0f },
|
||||
{ "else", 0f },
|
||||
{ "emit", 0f },
|
||||
{ "emit_stream", 0f },
|
||||
{ "emitthencut", 0f },
|
||||
{ "emitthencut_stream", 0f },
|
||||
{ "endif", 0f },
|
||||
{ "endloop", 0f },
|
||||
{ "endswitch", 0f },
|
||||
{ "fcall", 0f },
|
||||
{ "gather4", 0f },
|
||||
{ "gather4_c", 0f },
|
||||
{ "gather4_po", 0f },
|
||||
{ "gather4_po_c", 0f },
|
||||
{ "hs_control_point_phase", 0f },
|
||||
{ "hs_decls", 0f },
|
||||
{ "hs_fork_phase", 0f },
|
||||
{ "hs_join_phase", 0f },
|
||||
{ "if", 0f },
|
||||
{ "imm_atomic_alloc", 0f },
|
||||
{ "imm_atomic_and", 0f },
|
||||
{ "imm_atomic_cmp_exch", 0f },
|
||||
{ "imm_atomic_consume", 0f },
|
||||
{ "imm_atomic_exch", 0f },
|
||||
{ "imm_atomic_iadd", 0f },
|
||||
{ "imm_atomic_imax", 0f },
|
||||
{ "imm_atomic_imin", 0f },
|
||||
{ "imm_atomic_or", 0f },
|
||||
{ "imm_atomic_umax", 0f },
|
||||
{ "imm_atomic_umin", 0f },
|
||||
{ "imm_atomic_xor", 0f },
|
||||
{ "label", 0f },
|
||||
{ "ld", 0f },
|
||||
{ "ld_raw", 0f },
|
||||
{ "ld_structured", 0f },
|
||||
{ "ld_uav_typed", 0f },
|
||||
{ "ld2dms", 0f },
|
||||
{ "lod", 0f },
|
||||
{ "loop", 0f },
|
||||
{ "nop", 0f },
|
||||
{ "resinfo", 0f },
|
||||
{ "ret", 0f },
|
||||
{ "retc", 0f },
|
||||
{ "sample", 0f },
|
||||
{ "sample_b", 0f },
|
||||
{ "sample_c", 0f },
|
||||
{ "sample_c_lz", 0f },
|
||||
{ "sample_d", 0f },
|
||||
{ "sample_l", 0f },
|
||||
{ "sampleinfo", 0f },
|
||||
{ "samplepos", 0f },
|
||||
{ "store_raw", 0f },
|
||||
{ "store_structured", 0f },
|
||||
{ "store_uav_typed", 0f },
|
||||
{ "switch", 0f },
|
||||
{ "sync", 0f },
|
||||
};
|
||||
|
||||
private static int IndexOfWhitespace(string s)
|
||||
{
|
||||
for (int i = 0; i < s.Length; i++)
|
||||
{
|
||||
var c = s[i];
|
||||
if (char.IsWhiteSpace(c)) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static string NormalizeOpcode(string opcode)
|
||||
{
|
||||
// normalize to lower, strip optional _sat suffix used in some ops like mul_sat/mov_sat
|
||||
// opcode = opcode.ToLowerInvariant();
|
||||
if (opcode.EndsWith("_sat", StringComparison.Ordinal))
|
||||
opcode = opcode.Substring(0, opcode.Length - 3);
|
||||
return opcode;
|
||||
}
|
||||
|
||||
private static ShaderPerfStats ParseAsmStats(string asmText)
|
||||
{
|
||||
if (string.IsNullOrEmpty(asmText))
|
||||
{
|
||||
return new ShaderPerfStats();
|
||||
}
|
||||
|
||||
var totalCost = 0f;
|
||||
var sampleCount = 0;
|
||||
var samplers = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
var registerCount = 0;
|
||||
var interpChannels = 0;
|
||||
using (var reader = new StringReader(asmText))
|
||||
{
|
||||
int lineIndex = -1;
|
||||
while (reader.ReadLine() is { } line)
|
||||
{
|
||||
lineIndex++;
|
||||
line = line.Trim();
|
||||
|
||||
if (lineIndex < 2) continue;
|
||||
if (string.IsNullOrEmpty(line)) continue;
|
||||
if (line.StartsWith("//")) continue;
|
||||
|
||||
// TODO: Statistical flow control and other special instructions
|
||||
|
||||
var firstSpace = IndexOfWhitespace(line);
|
||||
// Skip headers like ps_#_# labels not considered
|
||||
if (firstSpace <= 0) continue;
|
||||
|
||||
var opcode = line.Substring(0, firstSpace).Trim();
|
||||
opcode = NormalizeOpcode(opcode);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(opcode)) continue;
|
||||
if (!char.IsLetter(opcode[0])) continue;
|
||||
|
||||
if (_opcodeWeight.TryGetValue(opcode, out var w))
|
||||
{
|
||||
totalCost += w;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"LWGUI: {typeof(ShaderCompilerDefaultFxc)}: Unknown opcode: {opcode}");
|
||||
}
|
||||
|
||||
// Texture sampling stats
|
||||
if (opcode.StartsWith("sample", StringComparison.Ordinal) || opcode.StartsWith("gather4", StringComparison.Ordinal))
|
||||
{
|
||||
sampleCount++;
|
||||
}
|
||||
else if (string.Equals(opcode, "ld", StringComparison.Ordinal))
|
||||
{
|
||||
// count ld reading from textures t#
|
||||
if (line.Contains(", t")) sampleCount++;
|
||||
}
|
||||
|
||||
// Sampler declarations
|
||||
if (opcode == "dcl_sampler")
|
||||
{
|
||||
// e.g. dcl_sampler s1, mode_default
|
||||
var idx = line.IndexOf('s');
|
||||
if (idx >= 0)
|
||||
{
|
||||
var end = idx + 1;
|
||||
while (end < line.Length && char.IsDigit(line[end])) end++;
|
||||
if (end > idx)
|
||||
{
|
||||
var sid = line.Substring(idx, end - idx);
|
||||
samplers.Add(sid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Temp registers
|
||||
if (opcode == "dcl_temps")
|
||||
{
|
||||
// e.g. dcl_temps 27
|
||||
var numStr = line.Substring(firstSpace).Trim();
|
||||
int.TryParse(numStr, NumberStyles.Integer, CultureInfo.InvariantCulture, out registerCount);
|
||||
}
|
||||
|
||||
// Interpolator channels: dcl_input_ps lines only
|
||||
if (opcode == "dcl_input_ps")
|
||||
{
|
||||
var dot = line.IndexOf('.');
|
||||
if (dot >= 0)
|
||||
{
|
||||
int count = 0;
|
||||
for (int i = dot + 1; i < line.Length; i++)
|
||||
{
|
||||
var c = line[i];
|
||||
if (c == 'x' || c == 'y' || c == 'z' || c == 'w') count++;
|
||||
else break;
|
||||
}
|
||||
interpChannels += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new ShaderPerfStats
|
||||
{
|
||||
estimatedCost = totalCost,
|
||||
sampleCount = sampleCount,
|
||||
samplerCount = samplers.Count,
|
||||
registerCount = registerCount,
|
||||
interpolatorChannelCount = interpChannels,
|
||||
isValid = true
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7b5553f7fbdc45d585e9ecd2c73d8539
|
||||
timeCreated: 1760616203
|
||||
Binary file not shown.
@ -1,6 +1,6 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 130ac8fd23814be49854157d7be9194e
|
||||
ShaderIncludeImporter:
|
||||
guid: 994434336edc8a8469c9afcbb92c5936
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
@ -0,0 +1,51 @@
|
||||
// Copyright (c) Jason Ma
|
||||
|
||||
using UnityEditor.Rendering;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEditor;
|
||||
|
||||
namespace LWGUI.PerformanceMonitor.ShaderCompiler
|
||||
{
|
||||
public interface IShaderCompiler
|
||||
{
|
||||
public static IShaderCompiler instance { get; }
|
||||
|
||||
public static bool isSupportCurrentPlatform => true;
|
||||
|
||||
public static int priority => 0;
|
||||
|
||||
public string compilerName { get; }
|
||||
|
||||
public ShaderCompilerPlatform api { get; set; }
|
||||
public BuildTarget target { get; set; }
|
||||
public GraphicsTier tier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The path to the Shader compilation result stored in text.
|
||||
/// </summary>
|
||||
public string GetCompiledShaderPath(ShaderPerfData shaderPerfData, string compiledShaderDirectory, string shaderTypeName);
|
||||
|
||||
/// <summary>
|
||||
/// Try to compile a pass variant. Returns true and outputs string on success.
|
||||
/// </summary>
|
||||
public bool CompilePass(ShaderPerfData shaderPerfData, ShaderData.Pass pass, ShaderType shaderType, string[] keywords,
|
||||
out string compiledShader);
|
||||
|
||||
/// <summary>
|
||||
/// Analyze a compiled shader (or readable asm) and return a compiler-specific stats object.
|
||||
/// The returned object is opaque to the caller and will be stored in ShaderPerfData.stats.
|
||||
/// Return null on failure.
|
||||
/// </summary>
|
||||
public object AnalyzeShaderPerformance(ShaderPerfData shaderPerfData, string compiledShader);
|
||||
|
||||
public void DrawShaderPerformanceStatsHeader(LWGUIMetaDatas metaDatas) { }
|
||||
|
||||
/// <summary>
|
||||
/// Draw a single line (pass) of shader performance UI inside the toolbar area.
|
||||
/// Compiler-specific UI (Find/Open buttons, label contents) should be implemented here.
|
||||
/// </summary>
|
||||
public void DrawShaderPerformanceStatsLine(LWGUIMetaDatas metaDatas, ShaderPerfData shaderPerfData);
|
||||
|
||||
public void DrawShaderPerformanceStatsFooter(LWGUIMetaDatas metaDatas) { }
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b7661dfeec673f340b66ac769fa42d18
|
||||
guid: 8719df0f3ab95f449b01b7c46d9bf80d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bf6617caaa864ee6854c54d837967c6d
|
||||
timeCreated: 1761656115
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e21bdb269db343c6b20d39dda87981e3
|
||||
timeCreated: 1761656119
|
||||
@ -0,0 +1,54 @@
|
||||
// Copyright (c) Jason Ma
|
||||
|
||||
using System;
|
||||
|
||||
namespace LWGUI.PerformanceMonitor.ShaderCompiler.Mali
|
||||
{
|
||||
[Serializable]
|
||||
public struct DynamicValue
|
||||
{
|
||||
public enum ValueType
|
||||
{
|
||||
Unknown,
|
||||
Null,
|
||||
Float,
|
||||
Int,
|
||||
Bool,
|
||||
}
|
||||
|
||||
public ValueType Type;
|
||||
public int Int;
|
||||
public float Float;
|
||||
public bool Bool;
|
||||
|
||||
public DynamicValue(object value)
|
||||
{
|
||||
if (value is long l)
|
||||
{
|
||||
value = (int) l;
|
||||
}
|
||||
|
||||
(Type, Int, Float, Bool) = value switch
|
||||
{
|
||||
int i => (ValueType.Int, i, 0, false),
|
||||
float f => (ValueType.Float, 0, f, false),
|
||||
bool b => (ValueType.Bool, 0, 0, b),
|
||||
null => (ValueType.Null, 0, 0, false),
|
||||
var _ => throw new ArgumentOutOfRangeException(nameof(value),
|
||||
$"Invalid object type: {value?.GetType()}"
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
public override string ToString() =>
|
||||
Type switch
|
||||
{
|
||||
ValueType.Unknown => throw new InvalidOperationException("Unknown value type"),
|
||||
ValueType.Float => Float.ToString("F2"),
|
||||
ValueType.Int => Int.ToString(),
|
||||
ValueType.Bool => Bool.ToString(),
|
||||
ValueType.Null => "N/A",
|
||||
_ => throw new ArgumentOutOfRangeException(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 082ebd2742584b6dbbfc47422310b203
|
||||
timeCreated: 1761656119
|
||||
@ -0,0 +1,66 @@
|
||||
// Copyright (c) Jason Ma
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
using System;
|
||||
|
||||
namespace LWGUI.PerformanceMonitor.ShaderCompiler.Mali
|
||||
{
|
||||
[Serializable]
|
||||
internal class JsonMaliocOutput
|
||||
{
|
||||
public Schema schema;
|
||||
public Shader[] shaders;
|
||||
|
||||
[Serializable]
|
||||
public class Schema
|
||||
{
|
||||
public string name;
|
||||
public int version;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Shader
|
||||
{
|
||||
// Normal output fields
|
||||
public ShaderProperty[] properties;
|
||||
public ShaderVariant[] variants;
|
||||
|
||||
// Error output fields
|
||||
public string[] errors;
|
||||
public string[] warnings;
|
||||
public string filename;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ShaderProperty
|
||||
{
|
||||
public string display_name;
|
||||
public string name;
|
||||
public string value;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ShaderVariant
|
||||
{
|
||||
public string name;
|
||||
public ShaderVariantPerformance performance;
|
||||
public ShaderProperty[] properties;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ShaderVariantPerformance
|
||||
{
|
||||
public string[] pipelines;
|
||||
public ShaderVariantCycles longest_path_cycles;
|
||||
public ShaderVariantCycles shortest_path_cycles;
|
||||
public ShaderVariantCycles total_cycles;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ShaderVariantCycles
|
||||
{
|
||||
public string[] bound_pipelines;
|
||||
public float[] cycle_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 97b5dd9724dd4e16b4cd3fade5b6b26e
|
||||
timeCreated: 1761656120
|
||||
@ -0,0 +1,59 @@
|
||||
// Copyright (c) Jason Ma
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace LWGUI.PerformanceMonitor.ShaderCompiler.Mali
|
||||
{
|
||||
[Serializable]
|
||||
public class RuntimeMaliocShader
|
||||
{
|
||||
[Serializable]
|
||||
public enum ShaderVariantPipelineType
|
||||
{
|
||||
Null,
|
||||
Arithmetic,
|
||||
LoadStore,
|
||||
Varying,
|
||||
Texture,
|
||||
}
|
||||
|
||||
public bool HasErrors;
|
||||
public List<string> Errors;
|
||||
public List<string> Warnings;
|
||||
public List<ShaderProperty> Properties;
|
||||
public List<ShaderVariant> Variants;
|
||||
|
||||
[Serializable]
|
||||
public class ShaderProperty
|
||||
{
|
||||
public enum Unit
|
||||
{
|
||||
None,
|
||||
Percent,
|
||||
}
|
||||
|
||||
public string Name;
|
||||
public DynamicValue Value;
|
||||
public Unit ValueUnit;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ShaderVariant
|
||||
{
|
||||
public string Name;
|
||||
public List<ShaderVariantPipelineType> Pipelines;
|
||||
public ShaderPipelineCycles LongestPathCycles;
|
||||
public ShaderPipelineCycles ShortestPathCycles;
|
||||
public ShaderPipelineCycles TotalCycles;
|
||||
public List<ShaderProperty> Properties;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ShaderPipelineCycles
|
||||
{
|
||||
public List<float> PipelineCycles;
|
||||
public ShaderVariantPipelineType BoundPipeline;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d803c9bc0cc4f1d9b50cb06b05aacb1
|
||||
timeCreated: 1761656119
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 24c5b23d58934748abe6deaa63777dd0
|
||||
timeCreated: 1761656120
|
||||
@ -0,0 +1,139 @@
|
||||
// Copyright (c) Jason Ma
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace LWGUI.PerformanceMonitor.ShaderCompiler.Mali
|
||||
{
|
||||
public static class MaliocOutputParser
|
||||
{
|
||||
public static RuntimeMaliocShader Parse(string json)
|
||||
{
|
||||
if (json == null)
|
||||
return null;
|
||||
|
||||
JsonMaliocOutput jsonModel;
|
||||
try
|
||||
{
|
||||
jsonModel = JsonUtility.FromJson<JsonMaliocOutput>(json);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"LWGUI: Failed to parse malioc json: {e.Message}");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (jsonModel == null || jsonModel.shaders == null || jsonModel.shaders.Length != 1)
|
||||
{
|
||||
Debug.LogError("LWGUI: malioc json missing shaders or unexpected format.");
|
||||
return null;
|
||||
}
|
||||
|
||||
var shader = jsonModel.shaders[0];
|
||||
|
||||
// Check if this is an error response
|
||||
bool isErrorResponse = jsonModel.schema?.name == "error";
|
||||
if (isErrorResponse || (shader.errors != null && shader.errors.Length > 0))
|
||||
{
|
||||
return new RuntimeMaliocShader
|
||||
{
|
||||
HasErrors = true,
|
||||
Errors = shader.errors?.ToList() ?? new System.Collections.Generic.List<string>(),
|
||||
Warnings = shader.warnings?.ToList() ?? new System.Collections.Generic.List<string>(),
|
||||
Properties = new System.Collections.Generic.List<RuntimeMaliocShader.ShaderProperty>(),
|
||||
Variants = new System.Collections.Generic.List<RuntimeMaliocShader.ShaderVariant>(),
|
||||
};
|
||||
}
|
||||
|
||||
return new RuntimeMaliocShader
|
||||
{
|
||||
HasErrors = false,
|
||||
Errors = new System.Collections.Generic.List<string>(),
|
||||
Warnings = shader.warnings?.ToList() ?? new System.Collections.Generic.List<string>(),
|
||||
Properties = shader.properties.Select(ConvertProperty).ToList(),
|
||||
Variants = shader.variants.Select(variant =>
|
||||
{
|
||||
var pipelines = variant.performance.pipelines.Select(ParsePipelineType).ToList();
|
||||
return new RuntimeMaliocShader.ShaderVariant
|
||||
{
|
||||
Name = variant.name,
|
||||
Properties = variant.properties.Select(ConvertVariantProperty).ToList(),
|
||||
Pipelines = pipelines,
|
||||
LongestPathCycles =
|
||||
ParseShaderPipelineCycles(variant.performance.longest_path_cycles),
|
||||
ShortestPathCycles =
|
||||
ParseShaderPipelineCycles(variant.performance.shortest_path_cycles),
|
||||
TotalCycles = ParseShaderPipelineCycles(variant.performance.total_cycles),
|
||||
};
|
||||
}
|
||||
).ToList(),
|
||||
};
|
||||
}
|
||||
|
||||
private static RuntimeMaliocShader.ShaderProperty ConvertProperty(JsonMaliocOutput.ShaderProperty property) =>
|
||||
new() { Name = property.display_name, Value = new DynamicValue(ParseValue(property.value)) };
|
||||
|
||||
private static RuntimeMaliocShader.ShaderProperty
|
||||
ConvertVariantProperty(JsonMaliocOutput.ShaderProperty property) =>
|
||||
new()
|
||||
{
|
||||
Name = property.display_name, Value = new DynamicValue(ParseValue(property.value)),
|
||||
ValueUnit = ParseValueUnit(property.name),
|
||||
};
|
||||
|
||||
private static RuntimeMaliocShader.ShaderProperty.Unit ParseValueUnit(string name) =>
|
||||
name switch
|
||||
{
|
||||
"thread_occupancy" => RuntimeMaliocShader.ShaderProperty.Unit.Percent,
|
||||
"fp16_arithmetic" => RuntimeMaliocShader.ShaderProperty.Unit.Percent,
|
||||
var _ => RuntimeMaliocShader.ShaderProperty.Unit.None,
|
||||
};
|
||||
|
||||
private static RuntimeMaliocShader.ShaderVariantPipelineType ParsePipelineType(string text) =>
|
||||
text switch
|
||||
{
|
||||
"arithmetic" => RuntimeMaliocShader.ShaderVariantPipelineType.Arithmetic,
|
||||
"load_store" => RuntimeMaliocShader.ShaderVariantPipelineType.LoadStore,
|
||||
"varying" => RuntimeMaliocShader.ShaderVariantPipelineType.Varying,
|
||||
"texture" => RuntimeMaliocShader.ShaderVariantPipelineType.Texture,
|
||||
"" => RuntimeMaliocShader.ShaderVariantPipelineType.Null,
|
||||
null => RuntimeMaliocShader.ShaderVariantPipelineType.Null,
|
||||
var _ => throw new ArgumentOutOfRangeException(nameof(text), text, "Invalid pipeline type."),
|
||||
};
|
||||
|
||||
private static RuntimeMaliocShader.ShaderPipelineCycles ParseShaderPipelineCycles(
|
||||
JsonMaliocOutput.ShaderVariantCycles cycles) =>
|
||||
new()
|
||||
{
|
||||
PipelineCycles = (cycles.cycle_count ?? Array.Empty<float>()).Select(f => f).ToList(),
|
||||
BoundPipeline = ParsePipelineType(cycles.bound_pipelines?.First()),
|
||||
};
|
||||
|
||||
private static object ParseValue(string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
return null;
|
||||
|
||||
var s = value.Trim();
|
||||
if (s.Equals("null", StringComparison.OrdinalIgnoreCase))
|
||||
return null;
|
||||
|
||||
// Try int
|
||||
if (int.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture, out var i))
|
||||
return i;
|
||||
|
||||
// Try float
|
||||
if (float.TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var f))
|
||||
return f;
|
||||
|
||||
// Try bool
|
||||
if (bool.TryParse(s, out var b))
|
||||
return b;
|
||||
|
||||
// Fallback: return string
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5b9ef44e450a4f9e81fc0f0d35afc38a
|
||||
timeCreated: 1761656120
|
||||
@ -0,0 +1,178 @@
|
||||
// Copyright (c) Jason Ma
|
||||
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEditor.Rendering;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using LWGUI.PerformanceMonitor.ShaderCompiler.Mali;
|
||||
|
||||
namespace LWGUI.PerformanceMonitor.ShaderCompiler
|
||||
{
|
||||
public class ShaderCompilerMali : IShaderCompiler
|
||||
{
|
||||
private static ShaderCompilerMali _instance;
|
||||
public static ShaderCompilerMali instance => _instance ??= new ShaderCompilerMali();
|
||||
|
||||
public ShaderCompilerPlatform api { get; set; } = ShaderCompilerPlatform.GLES3x;
|
||||
public BuildTarget target { get; set; } = BuildTarget.Android;
|
||||
public GraphicsTier tier { get; set; } = (GraphicsTier)(-1);
|
||||
|
||||
public string compilerName => "Malioc";
|
||||
|
||||
private static int _isSupportCurrentPlatform = -1;
|
||||
|
||||
public static bool isSupportCurrentPlatform
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_isSupportCurrentPlatform == -1)
|
||||
{
|
||||
if (!IOHelper.RunCMD("malioc --list", out var output) || string.IsNullOrWhiteSpace(output) || !output.Contains("Compiler"))
|
||||
_isSupportCurrentPlatform = 0;
|
||||
else
|
||||
_isSupportCurrentPlatform = 1;
|
||||
}
|
||||
|
||||
return _isSupportCurrentPlatform == 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static int priority => 10;
|
||||
|
||||
public string GetCompiledShaderPath(ShaderPerfData shaderPerfData, string compiledShaderDirectory, string shaderTypeName)
|
||||
{
|
||||
string ext;
|
||||
switch (shaderPerfData.shaderType)
|
||||
{
|
||||
// https://developer.arm.com/documentation/101863/8-8/Using-Mali-Offline-Compiler/Compiling-OpenGL-ES-shaders
|
||||
case ShaderType.Vertex: ext = ".vert"; break;
|
||||
case ShaderType.Fragment: ext = ".frag"; break;
|
||||
case ShaderType.Geometry: ext = ".geom"; break;
|
||||
default: return null;
|
||||
}
|
||||
|
||||
return Path.Combine(compiledShaderDirectory, $"Mali_{api}_{target}_{shaderTypeName}{ext}");
|
||||
}
|
||||
|
||||
public string GetMaliJsonOutputPath(ShaderPerfData shaderPerfData)
|
||||
=> Path.Combine(shaderPerfData.compiledShaderDirectory, $"Mali_{api}_{target}_{shaderPerfData.shaderTypeName}.json");
|
||||
|
||||
|
||||
public bool CompilePass(ShaderPerfData shaderPerfData, ShaderData.Pass pass, ShaderType shaderType, string[] keywords,
|
||||
out string compiledShader)
|
||||
{
|
||||
compiledShader = string.Empty;
|
||||
|
||||
if (shaderPerfData == null || pass == null || keywords == null)
|
||||
return false;
|
||||
|
||||
var compileInfo = pass.CompileVariant(shaderType, keywords, api, target, tier, true);
|
||||
if (!compileInfo.Success)
|
||||
return false;
|
||||
|
||||
compiledShader = Encoding.UTF8.GetString(compileInfo.ShaderData);
|
||||
|
||||
// Fix Mali Compiler Errors
|
||||
compiledShader = compiledShader.Replace("#version 300 es", "#version 320 es");
|
||||
compiledShader = compiledShader.Replace("#version 310 es", "#version 320 es");
|
||||
IOHelper.WriteTextFile(shaderPerfData.compiledShaderPath, compiledShader);
|
||||
|
||||
return !string.IsNullOrWhiteSpace(compiledShader);
|
||||
}
|
||||
|
||||
|
||||
public object AnalyzeShaderPerformance(ShaderPerfData shaderPerfData, string compiledShader)
|
||||
{
|
||||
var jsonPath = GetMaliJsonOutputPath(shaderPerfData);
|
||||
string jsonString;
|
||||
|
||||
if (!File.Exists(jsonPath))
|
||||
{
|
||||
// https://developer.arm.com/documentation/101863/8-8/Using-Mali-Offline-Compiler/Compiling-OpenGL-ES-shaders
|
||||
IOHelper.RunCMD($"malioc --core Mali-G76 --format json \"{shaderPerfData.compiledShaderPath}\"", out jsonString);
|
||||
IOHelper.WriteTextFile(jsonPath, jsonString);
|
||||
}
|
||||
else
|
||||
{
|
||||
jsonString = IOHelper.ReadTextFile(jsonPath);
|
||||
}
|
||||
|
||||
if (IOHelper.ExistAndNotEmpty(jsonPath))
|
||||
{
|
||||
return MaliocOutputParser.Parse(jsonString);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void DrawShaderPerformanceStatsHeader(LWGUIMetaDatas metaDatas)
|
||||
{
|
||||
EditorGUILayout.LabelField(" ", "Arithmetic Load/Store Varying Texture");
|
||||
}
|
||||
|
||||
// https://developer.arm.com/documentation/101863/8-8/Using-Mali-Offline-Compiler/Performance-analysis
|
||||
public void DrawShaderPerformanceStatsLine(LWGUIMetaDatas metaDatas, ShaderPerfData shaderPerfData)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
if (shaderPerfData.stats is RuntimeMaliocShader stats)
|
||||
{
|
||||
if (stats.Variants is { Count: > 0 } && stats.Variants[0].Pipelines is { Count: > 0 })
|
||||
{
|
||||
var variant = stats.Variants[0];
|
||||
var cycles = Enumerable.Repeat(0.0f, variant.Pipelines.Count).ToList();
|
||||
|
||||
for (int i = 0; i < variant.Pipelines.Count; i++)
|
||||
{
|
||||
cycles[i] = Mathf.Max(Mathf.Max(variant.ShortestPathCycles.PipelineCycles[i],
|
||||
variant.LongestPathCycles.PipelineCycles[i]),
|
||||
variant.TotalCycles.PipelineCycles[i]);
|
||||
}
|
||||
|
||||
// https://developer.arm.com/documentation/101863/8-8/Using-Mali-Offline-Compiler/Performance-analysis/Performance-table
|
||||
float arithmeticCycle = 0;
|
||||
float loadStoreCycle = 0;
|
||||
float varyingCycle = 0;
|
||||
float textureCycle = 0;
|
||||
for (int i = 0; i < variant.Pipelines.Count; i++)
|
||||
{
|
||||
switch (variant.Pipelines[i])
|
||||
{
|
||||
case RuntimeMaliocShader.ShaderVariantPipelineType.Arithmetic: arithmeticCycle = cycles[i]; break;
|
||||
case RuntimeMaliocShader.ShaderVariantPipelineType.LoadStore: loadStoreCycle = cycles[i]; break;
|
||||
case RuntimeMaliocShader.ShaderVariantPipelineType.Varying: varyingCycle = cycles[i]; break;
|
||||
case RuntimeMaliocShader.ShaderVariantPipelineType.Texture: textureCycle = cycles[i]; break;
|
||||
}
|
||||
}
|
||||
|
||||
var statsStr = $"{arithmeticCycle,8:0.0} {loadStoreCycle,9:0.0} {varyingCycle,7:0.0} {textureCycle,6:0.0}";
|
||||
EditorGUILayout.LabelField($"{shaderPerfData.passName} | {shaderPerfData.shaderTypeName}", statsStr, GUIStyles.label_monospace);
|
||||
|
||||
ToolbarHelper.DrawShaderPerformanceStatsLineButtons(shaderPerfData);
|
||||
if (GUILayout.Button("Json", GUILayout.MaxWidth(40)))
|
||||
IOHelper.OpenFile(GetMaliJsonOutputPath(shaderPerfData));
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.LabelField($"{shaderPerfData.passName} | {shaderPerfData.shaderTypeName}", "ANALYSIS FAILED");
|
||||
}
|
||||
|
||||
if (stats.HasErrors)
|
||||
{
|
||||
var errorMsg = stats.Errors.Count > 0 ? stats.Errors[0] : "Unknown Error";
|
||||
Debug.LogError($"LWGUI: {shaderPerfData.passName} | {shaderPerfData.shaderTypeName} Error:\n{errorMsg}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var status = shaderPerfData.isCompiledSuccessful ? "ANALYSIS FAILED" : "COMPILATION FAILED";
|
||||
EditorGUILayout.LabelField($"{shaderPerfData.passName} | {shaderPerfData.shaderTypeName}", status);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 40365616bf6b4d40bb751e848dba6105
|
||||
timeCreated: 1760616221
|
||||
@ -0,0 +1,27 @@
|
||||
// Copyright (c) Jason Ma
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.Rendering;
|
||||
|
||||
namespace LWGUI.PerformanceMonitor
|
||||
{
|
||||
public class ShaderPerfData
|
||||
{
|
||||
public int subshaderIndex = -1;
|
||||
public int passIndex = -1;
|
||||
public string passName = string.Empty;
|
||||
public ShaderType shaderType;
|
||||
public string hash = string.Empty;
|
||||
public bool isCompiledSuccessful = false;
|
||||
|
||||
// Path
|
||||
public string shaderTypeName = string.Empty;
|
||||
public string compiledShaderDirectory = string.Empty;
|
||||
public string compiledShaderPath = string.Empty;
|
||||
|
||||
// Opaque compiler-specific stats.
|
||||
// Compiler implementations should populate this with their own stats object (or null if unavailable).
|
||||
public object stats;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6adc39ef525045bb86847fb8e69ddbaf
|
||||
timeCreated: 1760615788
|
||||
@ -0,0 +1,253 @@
|
||||
// Copyright (c) Jason Ma
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using LWGUI.PerformanceMonitor.ShaderCompiler;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Rendering;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
namespace LWGUI.PerformanceMonitor
|
||||
{
|
||||
public static class ShaderPerfMonitor
|
||||
{
|
||||
#region Global Settings
|
||||
|
||||
public static GraphicsTier graphicsTier = (GraphicsTier)(-1);
|
||||
public static ShaderCompilerPlatform shaderCompilerPlatform = ShaderCompilerPlatform.D3D;
|
||||
public static BuildTarget buildTarget = BuildTarget.StandaloneWindows64;
|
||||
|
||||
#endregion
|
||||
|
||||
public static List<string> GetMaterialAndGlobalAndUserOverrideActiveKeywords(Material material, string shaderUID)
|
||||
{
|
||||
var output = new List<string>();
|
||||
|
||||
foreach (var localKeyword in material.enabledKeywords)
|
||||
{
|
||||
if (ToolbarHelper.IsUserKeywordOverride(shaderUID, localKeyword.name))
|
||||
{
|
||||
if (ToolbarHelper.IsUserKeywordEnabled(shaderUID, localKeyword.name))
|
||||
output.Add(localKeyword.name);
|
||||
}
|
||||
else
|
||||
{
|
||||
output.Add(localKeyword.name);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var keyword in material.shader.keywordSpace.keywords)
|
||||
{
|
||||
if (ToolbarHelper.IsUserKeywordOverride(shaderUID, keyword.name))
|
||||
{
|
||||
if (ToolbarHelper.IsUserKeywordEnabled(shaderUID, keyword.name))
|
||||
output.Add(keyword.name);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!keyword.isValid)
|
||||
continue;
|
||||
|
||||
// Is a global keyword?
|
||||
if (keyword.isOverridable && Shader.IsKeywordEnabled(keyword.name))
|
||||
output.Add(keyword.name);
|
||||
}
|
||||
}
|
||||
|
||||
return output.Distinct().ToList();
|
||||
}
|
||||
|
||||
public static string ComputeShaderVariantHash(List<string> keywords, BuildTarget target, ShaderCompilerPlatform platform, GraphicsTier tier)
|
||||
{
|
||||
var sorted = keywords?.OrderBy(k => k) ?? Enumerable.Empty<string>();
|
||||
var key = string.Join(";", sorted) + $"|{target}|{platform}|{tier}";
|
||||
using var md5 = MD5.Create();
|
||||
var bytes = Encoding.UTF8.GetBytes(key);
|
||||
var hash = md5.ComputeHash(bytes);
|
||||
return string.Concat(hash.Select(b => b.ToString("x2")));
|
||||
}
|
||||
|
||||
public static List<ShaderPerfData> GetShaderVariantPerfDatas(Shader shader, List<string> keywords)
|
||||
{
|
||||
var output = new List<ShaderPerfData>();
|
||||
var shaderData = ShaderUtil.GetShaderData(shader);
|
||||
var subshader = shaderData.ActiveSubshader;
|
||||
|
||||
IShaderCompiler compiler = GetActiveCompiler();
|
||||
|
||||
for (int i = 0; i < subshader.PassCount; i++)
|
||||
{
|
||||
var pass = subshader.GetPass(i);
|
||||
for (int j = 0; j < (int)ShaderType.Count; j++)
|
||||
{
|
||||
var shaderType = (ShaderType)j;
|
||||
if (!pass.HasShaderStage(shaderType))
|
||||
continue;
|
||||
|
||||
// Collect input data
|
||||
var hash = ComputeShaderVariantHash(keywords, buildTarget, shaderCompilerPlatform, graphicsTier);
|
||||
var shaderPerfData = new ShaderPerfData
|
||||
{
|
||||
subshaderIndex = shaderData.ActiveSubshaderIndex,
|
||||
passIndex = i,
|
||||
passName = IOHelper.GetValidFileName(pass.Name),
|
||||
shaderType = shaderType,
|
||||
hash = hash,
|
||||
};
|
||||
|
||||
shaderPerfData.shaderTypeName = shaderPerfData.shaderType.ToString();
|
||||
shaderPerfData.compiledShaderDirectory = IOHelper.GetCompiledShaderVariantCacheDirectory(shader, shaderPerfData);
|
||||
|
||||
if (compiler != null)
|
||||
{
|
||||
shaderPerfData.compiledShaderPath = compiler.GetCompiledShaderPath(shaderPerfData, shaderPerfData.compiledShaderDirectory, shaderPerfData.shaderTypeName);
|
||||
|
||||
if (!string.IsNullOrEmpty(shaderPerfData.compiledShaderPath))
|
||||
{
|
||||
// Compile and create cache
|
||||
shaderPerfData.isCompiledSuccessful = true;
|
||||
string compiledShader;
|
||||
if (!File.Exists(shaderPerfData.compiledShaderPath))
|
||||
{
|
||||
shaderPerfData.isCompiledSuccessful = compiler.CompilePass(shaderPerfData, pass, shaderType, keywords.ToArray(),
|
||||
out compiledShader);
|
||||
IOHelper.WriteTextFile(shaderPerfData.compiledShaderPath, compiledShader);
|
||||
}
|
||||
else
|
||||
{
|
||||
compiledShader = IOHelper.ReadTextFile(shaderPerfData.compiledShaderPath);
|
||||
}
|
||||
|
||||
shaderPerfData.isCompiledSuccessful &= IOHelper.ExistAndNotEmpty(shaderPerfData.compiledShaderPath);
|
||||
|
||||
// Analyze performance
|
||||
if (shaderPerfData.isCompiledSuccessful)
|
||||
{
|
||||
shaderPerfData.stats = compiler.AnalyzeShaderPerformance(shaderPerfData, compiledShader);
|
||||
|
||||
if (shaderPerfData.stats == null)
|
||||
Debug.LogError($"LWGUI: Failed to Analyze Shader: {shader.name} | Subshader: {shaderPerfData.subshaderIndex} | Pass: {shaderPerfData.passName} | Stage: {shaderType}\n" +
|
||||
$"Keywords: \n{string.Join('\n', keywords)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"LWGUI: Failed to Compile Shader: {shader.name} | Subshader: {shaderPerfData.subshaderIndex} | Pass: {shaderPerfData.passName} | Stage: {shaderType}\n" +
|
||||
$"Keywords: \n{string.Join('\n', keywords)}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("LWGUI: Unable to get the compiled Shader path!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
output.Add(shaderPerfData);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public static void ClearShaderPerfCache(Shader shader)
|
||||
{
|
||||
IOHelper.ClearShaderPerfCache(shader);
|
||||
}
|
||||
|
||||
private static IShaderCompiler _cachedActiveCompiler;
|
||||
private static readonly object _compilerLock = new object();
|
||||
|
||||
public static IShaderCompiler GetActiveCompiler()
|
||||
{
|
||||
if (_cachedActiveCompiler != null)
|
||||
return _cachedActiveCompiler;
|
||||
|
||||
lock (_compilerLock)
|
||||
{
|
||||
if (_cachedActiveCompiler != null)
|
||||
return _cachedActiveCompiler;
|
||||
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
var compilerTypes = assembly.GetTypes()
|
||||
.Where(t => !t.IsInterface && !t.IsAbstract && typeof(IShaderCompiler).IsAssignableFrom(t))
|
||||
.ToList();
|
||||
|
||||
Type bestType = null;
|
||||
int bestPriority = int.MinValue;
|
||||
|
||||
foreach (var compilerType in compilerTypes)
|
||||
{
|
||||
try
|
||||
{
|
||||
var isSupportProp = compilerType.GetProperty("isSupportCurrentPlatform",
|
||||
BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
|
||||
if (isSupportProp == null)
|
||||
continue;
|
||||
|
||||
bool isSupported = (bool)isSupportProp.GetValue(null);
|
||||
if (!isSupported)
|
||||
continue;
|
||||
|
||||
var priorityProp = compilerType.GetProperty("priority",
|
||||
BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
|
||||
int priority = 0;
|
||||
if (priorityProp != null)
|
||||
{
|
||||
try { priority = (int)priorityProp.GetValue(null); } catch { priority = 0; }
|
||||
}
|
||||
|
||||
if (bestType == null || priority > bestPriority)
|
||||
{
|
||||
bestType = compilerType;
|
||||
bestPriority = priority;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"LWGUI: Error inspecting compiler type {compilerType.Name}: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
if (bestType != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var instanceProp = bestType.GetProperty("instance",
|
||||
BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
|
||||
if (instanceProp != null)
|
||||
{
|
||||
var instance = instanceProp.GetValue(null) as IShaderCompiler;
|
||||
if (instance != null)
|
||||
{
|
||||
_cachedActiveCompiler = instance;
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"LWGUI: Error getting compiler instance of type {bestType.Name}: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
Debug.LogWarning("LWGUI: No supported shader compiler found!");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void ResetActiveCompiler()
|
||||
{
|
||||
lock (_compilerLock)
|
||||
{
|
||||
_cachedActiveCompiler = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 626fb2fbefe34f578241fed5232ed380
|
||||
timeCreated: 1760615736
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright (c) Jason Ma
|
||||
// Copyright (c) Jason Ma
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
// Copyright (c) Jason Ma
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@ -7,44 +8,186 @@ using LWGUI.LwguiGradientEditor;
|
||||
using LWGUI.Runtime.LwguiGradient;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
|
||||
namespace LWGUI
|
||||
{
|
||||
[CreateAssetMenu(fileName = "LWGUI_RampAtlas.asset", menuName = "LWGUI/Ramp Atlas", order = 84)]
|
||||
public class LwguiRampAtlas : ScriptableObject
|
||||
public interface IRamp
|
||||
{
|
||||
[Serializable]
|
||||
public class Ramp
|
||||
string Name { get; set; }
|
||||
ColorSpace ColorSpace { get; set; }
|
||||
LwguiGradient.ChannelMask ChannelMask { get; set; }
|
||||
LwguiGradient.GradientTimeRange TimeRange { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ramp contains at least one Gradient.
|
||||
/// You can add more custom Gradients by overriding the virtual functions in LwguiRampAtlas.
|
||||
/// </summary>
|
||||
LwguiGradient Gradient { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get All Gradients.
|
||||
/// </summary>
|
||||
LwguiGradient[] GetGradients();
|
||||
|
||||
/// <summary>
|
||||
/// Fill pixels for this Ramp into the output array.
|
||||
/// </summary>
|
||||
/// <param name="outputPixels">The output pixel array to fill</param>
|
||||
/// <param name="currentIndex">Current index in the output array, will be updated after filling</param>
|
||||
/// <param name="width">Width of each row in pixels</param>
|
||||
void GetPixelsForAtlas(ref Color[] outputPixels, ref int currentIndex, int width);
|
||||
|
||||
/// <summary>
|
||||
/// Get preview textures for this Ramp in Ramp Selector Window.
|
||||
/// Returns multiple textures if the Ramp contains multiple gradients.
|
||||
/// </summary>
|
||||
/// <param name="width">Width of the preview texture</param>
|
||||
/// <returns>Array of preview textures</returns>
|
||||
Texture2D[] GetPreviewTexturesForRampSelector(int width);
|
||||
|
||||
/// <summary>
|
||||
/// Copy properties from another Ramp.
|
||||
/// </summary>
|
||||
/// <param name="source">The source Ramp to copy from</param>
|
||||
void CopyFrom(IRamp source);
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Ramp : IRamp
|
||||
{
|
||||
public string name = "New Ramp";
|
||||
public ColorSpace colorSpace = ColorSpace.Gamma;
|
||||
public LwguiGradient.ChannelMask channelMask = LwguiGradient.ChannelMask.All;
|
||||
public LwguiGradient.GradientTimeRange timeRange = LwguiGradient.GradientTimeRange.One;
|
||||
public LwguiGradient gradient = LwguiGradient.white;
|
||||
|
||||
// IRamp interface implementation using explicit properties that wrap the fields
|
||||
string IRamp.Name { get => name; set => name = value; }
|
||||
ColorSpace IRamp.ColorSpace { get => colorSpace; set => colorSpace = value; }
|
||||
LwguiGradient.ChannelMask IRamp.ChannelMask { get => channelMask; set => channelMask = value; }
|
||||
LwguiGradient.GradientTimeRange IRamp.TimeRange { get => timeRange; set => timeRange = value; }
|
||||
LwguiGradient IRamp.Gradient { get => gradient; set => gradient = value; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of rows this Ramp type occupies in the Ramp Atlas Texture.
|
||||
/// This is a static value bound to the type. Derived classes can hide this with 'new static'.
|
||||
/// </summary>
|
||||
public static int RowCount => 1;
|
||||
|
||||
public virtual LwguiGradient[] GetGradients() => new[] { gradient };
|
||||
|
||||
public virtual void GetPixelsForAtlas(ref Color[] outputPixels, ref int currentIndex, int width)
|
||||
{
|
||||
public string name = "New Ramp";
|
||||
public LwguiGradient gradient = LwguiGradient.white;
|
||||
public ColorSpace colorSpace = ColorSpace.Gamma;
|
||||
public LwguiGradient.ChannelMask channelMask = LwguiGradient.ChannelMask.All;
|
||||
public LwguiGradient.GradientTimeRange timeRange = LwguiGradient.GradientTimeRange.One;
|
||||
var gradients = GetGradients();
|
||||
for (int i = 0; i < gradients.Length; i++)
|
||||
{
|
||||
gradients[i]?.GetPixels(ref outputPixels, ref currentIndex, width, 1, channelMask);
|
||||
}
|
||||
}
|
||||
|
||||
public const string RampAtlasSOExtensionName = "asset";
|
||||
|
||||
public virtual Texture2D[] GetPreviewTexturesForRampSelector(int width)
|
||||
{
|
||||
var gradients = GetGradients();
|
||||
var textures = new Texture2D[gradients.Length];
|
||||
for (int i = 0; i < gradients.Length; i++)
|
||||
{
|
||||
textures[i] = gradients[i]?.GetPreviewRampTexture(width, 1, colorSpace, channelMask);
|
||||
}
|
||||
return textures;
|
||||
}
|
||||
|
||||
public virtual void CopyFrom(IRamp source)
|
||||
{
|
||||
if (source == null) return;
|
||||
|
||||
name = source.Name;
|
||||
colorSpace = source.ColorSpace;
|
||||
channelMask = source.ChannelMask;
|
||||
timeRange = source.TimeRange;
|
||||
gradient = new LwguiGradient(source.Gradient);
|
||||
|
||||
var gradients = GetGradients();
|
||||
for (int i = 1; i < gradients.Length; i++)
|
||||
{
|
||||
if (gradients[i] == null)
|
||||
continue;
|
||||
|
||||
gradients[i].DeepCopyFrom(source.Gradient);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[CreateAssetMenu(fileName = "LWGUI_RampAtlas.asset", menuName = "LWGUI/Ramp Atlas", order = 84)]
|
||||
public partial class LwguiRampAtlas : ScriptableObject
|
||||
{
|
||||
public const string RampAtlasSOExtensionName = "asset";
|
||||
public const string RampAtlasTextureExtensionName = "tga";
|
||||
|
||||
public int rampAtlasWidth = 256;
|
||||
public int rampAtlasHeight = 4;
|
||||
public bool rampAtlasSRGB = true;
|
||||
|
||||
public int rampAtlasWidth = 256;
|
||||
public int rampAtlasHeight = 4;
|
||||
public bool rampAtlasSRGB = true;
|
||||
private string _rampAtlasSOPath = string.Empty;
|
||||
private string _rampAtlasTexturePath = string.Empty;
|
||||
|
||||
[SerializeField] private bool _saveTextureToggle;
|
||||
[SerializeField] private List<Ramp> _ramps = new List<Ramp>();
|
||||
|
||||
[NonSerialized] public Texture2D rampAtlasTexture = null;
|
||||
|
||||
[SerializeField] private List<Ramp> _ramps = new List<Ramp>();
|
||||
public List<Ramp> ramps
|
||||
{
|
||||
get => _ramps ?? new List<Ramp>();
|
||||
|
||||
set => _ramps = value ?? new List<Ramp>();
|
||||
#region Ramp Operate
|
||||
|
||||
/// <summary>
|
||||
/// Access the list of Ramps as IRamp interface. This property uses covariance (IReadOnlyList),
|
||||
/// allowing derived classes to directly return their strongly-typed lists without type conversion.
|
||||
/// Override this property in derived classes to return a custom Ramp list.
|
||||
/// </summary>
|
||||
public virtual IReadOnlyList<IRamp> Ramps => _ramps ??= new List<Ramp>();
|
||||
|
||||
public virtual int RowCountPerRamp => Ramp.RowCount;
|
||||
|
||||
public int RampCount => Ramps.Count;
|
||||
|
||||
public int TotalRowCount => RampCount * RowCountPerRamp;
|
||||
|
||||
public virtual IRamp CreateRamp(IRamp srcRamp = null)
|
||||
{
|
||||
var newRamp = new Ramp();
|
||||
if (srcRamp != null)
|
||||
newRamp.CopyFrom(srcRamp);
|
||||
return newRamp;
|
||||
}
|
||||
|
||||
[SerializeField] private bool _saveTextureToggle;
|
||||
private string _rampAtlasSOPath = string.Empty;
|
||||
private string _rampAtlasTexturePath = string.Empty;
|
||||
public virtual IRamp AddRamp(IRamp srcRamp = null)
|
||||
{
|
||||
_ramps ??= new List<Ramp>();
|
||||
var newRamp = CreateRamp(srcRamp);
|
||||
_ramps.Add(newRamp as Ramp);
|
||||
return newRamp;
|
||||
}
|
||||
|
||||
public virtual IRamp GetRamp(int index)
|
||||
{
|
||||
if (index < RampCount && index >= 0)
|
||||
{
|
||||
return Ramps[index];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public virtual void ClearRamps()
|
||||
{
|
||||
_ramps?.Clear();
|
||||
}
|
||||
|
||||
public void CheckRampRowCount()
|
||||
{
|
||||
if (TotalRowCount > rampAtlasHeight)
|
||||
Debug.LogError($"LWGUI: Ramp Atlas does NOT have enough height ({rampAtlasHeight} < {TotalRowCount}):\n{_rampAtlasSOPath}");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void InitData()
|
||||
{
|
||||
@ -59,12 +202,16 @@ namespace LWGUI
|
||||
{
|
||||
if (!AssetDatabase.Contains(this))
|
||||
return false;
|
||||
|
||||
|
||||
// Try to load
|
||||
rampAtlasTexture = AssetDatabase.LoadAssetAtPath<Texture2D>(_rampAtlasTexturePath);
|
||||
|
||||
// Create
|
||||
if (!rampAtlasTexture)
|
||||
if (!rampAtlasTexture
|
||||
|| rampAtlasTexture.width != rampAtlasWidth
|
||||
|| rampAtlasTexture.height != rampAtlasHeight
|
||||
|| rampAtlasTexture.isDataSRGB != rampAtlasSRGB
|
||||
)
|
||||
{
|
||||
CreateRampAtlasTexture();
|
||||
rampAtlasTexture = AssetDatabase.LoadAssetAtPath<Texture2D>(_rampAtlasTexturePath);
|
||||
@ -72,69 +219,51 @@ namespace LWGUI
|
||||
|
||||
if (!rampAtlasTexture)
|
||||
{
|
||||
Debug.LogError($"LWGUI: Can NOT create a Ramp Atlas Texture at path: { _rampAtlasTexturePath }");
|
||||
Debug.LogError($"LWGUI: Can NOT create a Ramp Atlas Texture at path: {_rampAtlasTexturePath}");
|
||||
return false;
|
||||
}
|
||||
|
||||
CheckRampRowCount();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public Color[] GetPixels()
|
||||
public virtual Color[] GetPixels()
|
||||
{
|
||||
Color[] pixels = Enumerable.Repeat(Color.white, rampAtlasWidth * rampAtlasHeight).ToArray();
|
||||
var pixels = Enumerable.Repeat(Color.white, rampAtlasWidth * rampAtlasHeight).ToArray();
|
||||
int currentIndex = 0;
|
||||
foreach (var ramp in ramps)
|
||||
foreach (var ramp in Ramps)
|
||||
{
|
||||
ramp.gradient.GetPixels(ref pixels, ref currentIndex, rampAtlasWidth, 1, ramp.channelMask);
|
||||
ramp?.GetPixelsForAtlas(ref pixels, ref currentIndex, rampAtlasWidth);
|
||||
}
|
||||
|
||||
return pixels;
|
||||
}
|
||||
|
||||
public Texture2D[] GetTexture2Ds(LwguiGradient.ChannelMask channelMask = LwguiGradient.ChannelMask.All)
|
||||
{
|
||||
Texture2D[] textures = new Texture2D[ramps.Count];
|
||||
for (int i = 0; i < ramps.Count; i++)
|
||||
{
|
||||
var ramp = ramps[i];
|
||||
textures[i] = Instantiate(ramp.gradient?.GetPreviewRampTexture(rampAtlasWidth, 1, ramp.colorSpace, ramp.channelMask & channelMask));
|
||||
textures[i].name = ramp.name;
|
||||
}
|
||||
|
||||
return textures;
|
||||
}
|
||||
|
||||
public Ramp GetRamp(int index)
|
||||
{
|
||||
if (index < ramps.Count && index >= 0)
|
||||
{
|
||||
return ramps[index] ?? new Ramp();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void CreateRampAtlasTexture()
|
||||
{
|
||||
var rampAtlasTexture = new Texture2D(rampAtlasWidth, rampAtlasHeight, TextureFormat.RGBA32, false, !rampAtlasSRGB);
|
||||
rampAtlasTexture.SetPixels(GetPixels());
|
||||
rampAtlasTexture.wrapMode = TextureWrapMode.Clamp;
|
||||
rampAtlasTexture.name = Path.GetFileName(_rampAtlasTexturePath);
|
||||
rampAtlasTexture.Apply();
|
||||
var rampAtlas = new Texture2D(rampAtlasWidth, rampAtlasHeight, TextureFormat.RGBA32, false, !rampAtlasSRGB);
|
||||
rampAtlas.SetPixels(GetPixels());
|
||||
rampAtlas.wrapMode = TextureWrapMode.Clamp;
|
||||
rampAtlas.name = Path.GetFileName(_rampAtlasTexturePath);
|
||||
rampAtlas.Apply();
|
||||
|
||||
SaveTexture(rampAtlasTexture);
|
||||
SaveTexture(rampAtlas, checkoutAndForceWrite:true);
|
||||
|
||||
AssetDatabase.ImportAsset(_rampAtlasTexturePath);
|
||||
RampHelper.SetRampTextureImporter(_rampAtlasTexturePath, true, !rampAtlasSRGB, EditorJsonUtility.ToJson(this));
|
||||
}
|
||||
|
||||
public void SaveTexture(Texture2D rampAtlasTexture = null, string targetRelativePath = null, bool checkoutAndForceWrite = false)
|
||||
public void SaveTexture(Texture2D rampAtlas = null, string targetRelativePath = null, bool checkoutAndForceWrite = false)
|
||||
{
|
||||
targetRelativePath ??= _rampAtlasTexturePath;
|
||||
rampAtlasTexture ??= this.rampAtlasTexture;
|
||||
if (!rampAtlasTexture || string.IsNullOrEmpty(targetRelativePath))
|
||||
rampAtlas ??= rampAtlasTexture;
|
||||
if (!rampAtlas || string.IsNullOrEmpty(targetRelativePath))
|
||||
return;
|
||||
|
||||
var absPath = Helper.ProjectPath + targetRelativePath;
|
||||
CheckRampRowCount();
|
||||
|
||||
var absPath = IOHelper.GetAbsPath(targetRelativePath);
|
||||
if (File.Exists(absPath))
|
||||
{
|
||||
var existRampTexture = AssetDatabase.LoadAssetAtPath<Texture2D>(targetRelativePath);
|
||||
@ -144,7 +273,7 @@ namespace LWGUI
|
||||
{
|
||||
if (!VersionControlHelper.Checkout(targetRelativePath))
|
||||
{
|
||||
Debug.LogError($"LWGUI: Can NOT write the Ramp Atlas Texture to path: { absPath }");
|
||||
Debug.LogError($"LWGUI: Can NOT write the Ramp Atlas Texture to path: {absPath}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -157,10 +286,10 @@ namespace LWGUI
|
||||
|
||||
try
|
||||
{
|
||||
File.WriteAllBytes(absPath, rampAtlasTexture.EncodeToTGA());
|
||||
File.WriteAllBytes(absPath, rampAtlas.EncodeToTGA());
|
||||
SaveTextureUserData(targetRelativePath);
|
||||
|
||||
Debug.Log($"LWGUI: Saved the Ramp Atlas Texture at path: { absPath }");
|
||||
|
||||
Debug.Log($"LWGUI: Saved the Ramp Atlas Texture at path: {absPath}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -191,58 +320,26 @@ namespace LWGUI
|
||||
{
|
||||
if (!rampAtlasTexture)
|
||||
return;
|
||||
|
||||
|
||||
LwguiGradientWindow.RegisterSerializedObjectUndo(this);
|
||||
rampAtlasTexture.Reinitialize(rampAtlasWidth, rampAtlasHeight);
|
||||
rampAtlasTexture.SetPixels(GetPixels());
|
||||
rampAtlasTexture.Apply();
|
||||
}
|
||||
|
||||
|
||||
public void DiscardChanges()
|
||||
{
|
||||
var importer = AssetImporter.GetAtPath(_rampAtlasTexturePath);
|
||||
if (!importer)
|
||||
return;
|
||||
|
||||
|
||||
EditorJsonUtility.FromJsonOverwrite(importer.userData, this);
|
||||
InitData();
|
||||
AssetDatabase.ImportAsset(_rampAtlasTexturePath, ImportAssetOptions.ForceUpdate);
|
||||
LoadTexture();
|
||||
EditorUtility.ClearDirty(this);
|
||||
}
|
||||
|
||||
public void ConvertColorSpace(ColorSpace targetColorSpace)
|
||||
{
|
||||
foreach (var ramp in ramps)
|
||||
{
|
||||
if (ramp.colorSpace != targetColorSpace)
|
||||
{
|
||||
ramp.colorSpace = targetColorSpace;
|
||||
ramp.gradient.ConvertColorSpaceWithoutCopy(
|
||||
targetColorSpace != ColorSpace.Gamma
|
||||
? ColorSpace.Linear
|
||||
: ColorSpace.Gamma);
|
||||
}
|
||||
}
|
||||
|
||||
rampAtlasSRGB = targetColorSpace == ColorSpace.Gamma;
|
||||
RampHelper.SetRampTextureImporter(_rampAtlasTexturePath, true, !rampAtlasSRGB, EditorJsonUtility.ToJson(this));
|
||||
UpdateTexturePixels();
|
||||
SaveTexture();
|
||||
}
|
||||
|
||||
[ContextMenu("Convert Gamma To Linear")]
|
||||
public void ConvertGammaToLinear()
|
||||
{
|
||||
ConvertColorSpace(ColorSpace.Linear);
|
||||
}
|
||||
|
||||
[ContextMenu("Convert Linear To Gamma")]
|
||||
public void ConvertLinearToGamma()
|
||||
{
|
||||
ConvertColorSpace(ColorSpace.Gamma);
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
InitData();
|
||||
@ -253,18 +350,21 @@ namespace LWGUI
|
||||
{
|
||||
// Skip at the end of compilation
|
||||
if (Event.current == null
|
||||
// Skip when editing Text Field
|
||||
|| EditorGUIUtility.editingTextField)
|
||||
// Skip when editing Text Field
|
||||
|| EditorGUIUtility.editingTextField)
|
||||
return;
|
||||
|
||||
|
||||
InitData();
|
||||
|
||||
|
||||
if (!LoadTexture())
|
||||
return;
|
||||
|
||||
UpdateTexturePixels();
|
||||
SaveTexture();
|
||||
SaveTexture(checkoutAndForceWrite:_saveTextureToggle);
|
||||
_saveTextureToggle = false;
|
||||
}
|
||||
|
||||
#region Static
|
||||
|
||||
public static Texture LoadRampAtlasTexture(LwguiRampAtlas rampAtlasSO)
|
||||
{
|
||||
@ -272,11 +372,11 @@ namespace LWGUI
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
var soPath = Path.ChangeExtension(AssetDatabase.GetAssetPath(rampAtlasSO), RampAtlasTextureExtensionName);
|
||||
return AssetDatabase.LoadAssetAtPath<Texture>(soPath);
|
||||
}
|
||||
|
||||
|
||||
public static LwguiRampAtlas LoadRampAtlasSO(Texture texture)
|
||||
{
|
||||
if (!texture || !AssetDatabase.Contains(texture))
|
||||
@ -287,17 +387,17 @@ namespace LWGUI
|
||||
var soPath = Path.ChangeExtension(AssetDatabase.GetAssetPath(texture), RampAtlasSOExtensionName);
|
||||
return AssetDatabase.LoadAssetAtPath<LwguiRampAtlas>(soPath);
|
||||
}
|
||||
|
||||
public static LwguiRampAtlas CreateRampAtlasSO(MaterialProperty rampAtlasProp, LWGUIMetaDatas metaDatas)
|
||||
|
||||
public static LwguiRampAtlas CreateRampAtlasSO(MaterialProperty rampAtlasProp, LWGUIMetaDatas metaDatas, Type rampAtlasType = null)
|
||||
{
|
||||
if (rampAtlasProp == null || metaDatas == null)
|
||||
return null;
|
||||
|
||||
var shader = metaDatas.GetShader();
|
||||
|
||||
|
||||
// Get default ramps
|
||||
RampAtlasDrawer targetRampAtlasDrawer = null;
|
||||
List<(int defaultIndex, RampAtlasIndexerDrawer indexerDrawer)> defaultRampAtlasIndexerDrawers = new ();
|
||||
List<(int defaultIndex, RampAtlasIndexerDrawer indexerDrawer)> defaultRampAtlasIndexerDrawers = new();
|
||||
// Unity Bug: The cache of MaterialPropertyHandler must be cleared first, otherwise the default value cannot be obtained correctly.
|
||||
ReflectionHelper.InvalidatePropertyCache(shader);
|
||||
for (int i = 0; i < metaDatas.perMaterialData.defaultPropertiesWithPresetOverride.Length; i++)
|
||||
@ -309,19 +409,25 @@ namespace LWGUI
|
||||
|
||||
if (drawer is RampAtlasDrawer rampAtlasDrawer && prop.name == rampAtlasProp.name)
|
||||
targetRampAtlasDrawer = rampAtlasDrawer;
|
||||
|
||||
|
||||
if (drawer is RampAtlasIndexerDrawer rampAtlasIndexerDrawer && rampAtlasIndexerDrawer.rampAtlasPropName == rampAtlasProp.name)
|
||||
defaultRampAtlasIndexerDrawers.Add(((int)prop.GetNumericValue(), rampAtlasIndexerDrawer));
|
||||
}
|
||||
|
||||
|
||||
if (targetRampAtlasDrawer == null)
|
||||
{
|
||||
Debug.LogError($"LWGUI: Can NOT find RampAtlasDrawer { rampAtlasProp.name } in Shader { shader }");
|
||||
Debug.LogError($"LWGUI: Can NOT find RampAtlasDrawer {rampAtlasProp.name} in Shader {shader}");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Init Ramp Atlas with custom type or default type
|
||||
rampAtlasType ??= typeof(LwguiRampAtlas);
|
||||
var newRampAtlasSO = ScriptableObject.CreateInstance(rampAtlasType) as LwguiRampAtlas;
|
||||
if (newRampAtlasSO == null)
|
||||
{
|
||||
Debug.LogError($"LWGUI: Failed to create RampAtlas of type '{rampAtlasType.Name}'");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Init Ramp Atlas
|
||||
var newRampAtlasSO = ScriptableObject.CreateInstance<LwguiRampAtlas>();
|
||||
newRampAtlasSO.name = targetRampAtlasDrawer.defaultFileName;
|
||||
newRampAtlasSO.rampAtlasWidth = targetRampAtlasDrawer.defaultAtlasWidth;
|
||||
newRampAtlasSO.rampAtlasHeight = targetRampAtlasDrawer.defaultAtlasHeight;
|
||||
@ -335,36 +441,50 @@ namespace LWGUI
|
||||
var maxIndex = defaultRampAtlasIndexerDrawers.Max((tuple => tuple.defaultIndex));
|
||||
for (int i = 0; i < maxIndex + 1; i++)
|
||||
{
|
||||
newRampAtlasSO.ramps.Add(new LwguiRampAtlas.Ramp());
|
||||
if (newRampAtlasSO.ramps.Count >= newRampAtlasSO.rampAtlasHeight)
|
||||
newRampAtlasSO.AddRamp();
|
||||
if (newRampAtlasSO.TotalRowCount > newRampAtlasSO.rampAtlasHeight)
|
||||
newRampAtlasSO.rampAtlasHeight *= 2;
|
||||
}
|
||||
|
||||
|
||||
// Set Ramps Default Value
|
||||
for (int i = 0; i < defaultRampAtlasIndexerDrawers.Count; i++)
|
||||
{
|
||||
var defaultRampAtlasIndexerDrawer = defaultRampAtlasIndexerDrawers[i];
|
||||
var ramp = newRampAtlasSO.ramps[defaultRampAtlasIndexerDrawer.defaultIndex];
|
||||
var ramp = newRampAtlasSO.GetRamp(defaultRampAtlasIndexerDrawer.defaultIndex);
|
||||
var drawer = defaultRampAtlasIndexerDrawer.indexerDrawer;
|
||||
ramp.name = drawer.defaultRampName;
|
||||
ramp.colorSpace = drawer.colorSpace;
|
||||
ramp.channelMask = drawer.viewChannelMask;
|
||||
ramp.timeRange = drawer.timeRange;
|
||||
ramp.Name = drawer.defaultRampName;
|
||||
ramp.ColorSpace = drawer.colorSpace;
|
||||
ramp.ChannelMask = drawer.viewChannelMask;
|
||||
ramp.TimeRange = drawer.timeRange;
|
||||
}
|
||||
}
|
||||
|
||||
return SaveRampAtlasSOToAsset(newRampAtlasSO, targetRampAtlasDrawer.rootPath, targetRampAtlasDrawer.defaultFileName);
|
||||
}
|
||||
|
||||
public static LwguiRampAtlas CloneRampAtlasSO(LwguiRampAtlas rampAtlasSO)
|
||||
public static LwguiRampAtlas CloneRampAtlasSO(LwguiRampAtlas rampAtlasSO, Type targetType = null)
|
||||
{
|
||||
if (!rampAtlasSO)
|
||||
return null;
|
||||
|
||||
var newRampAtlasSO = Instantiate(rampAtlasSO);
|
||||
var rootPath = Path.GetDirectoryName(rampAtlasSO._rampAtlasSOPath);
|
||||
var defaultFileName = Path.GetFileName(rampAtlasSO._rampAtlasSOPath);
|
||||
|
||||
LwguiRampAtlas newRampAtlasSO;
|
||||
|
||||
// If target type is specified and different from source type, use ConvertToType
|
||||
if (targetType != null && targetType != rampAtlasSO.GetType())
|
||||
{
|
||||
// Use ConvertToType for type conversion
|
||||
newRampAtlasSO = ConvertToType(rampAtlasSO, targetType, false);
|
||||
return newRampAtlasSO;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Same type, use Instantiate for direct clone
|
||||
newRampAtlasSO = Instantiate(rampAtlasSO);
|
||||
}
|
||||
|
||||
if (SaveRampAtlasSOToAsset(newRampAtlasSO, rootPath, defaultFileName))
|
||||
{
|
||||
newRampAtlasSO.InitData();
|
||||
@ -374,7 +494,7 @@ namespace LWGUI
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static LwguiRampAtlas SaveRampAtlasSOToAsset(LwguiRampAtlas rampAtlasSO, string rootPath, string defaultFileName)
|
||||
{
|
||||
if (!rampAtlasSO)
|
||||
@ -387,15 +507,15 @@ namespace LWGUI
|
||||
// TODO: Warning:
|
||||
// PropertiesGUI() is being called recursively. If you want to render the default gui for shader properties then call PropertiesDefaultGUI() instead
|
||||
var absPath = EditorUtility.SaveFilePanel("Create a Ramp Atlas SO", rootPath, defaultFileName, "asset");
|
||||
|
||||
if (absPath.StartsWith(Helper.ProjectPath))
|
||||
|
||||
if (absPath.StartsWith(IOHelper.ProjectPath))
|
||||
{
|
||||
createdFileRelativePath = absPath.Replace(Helper.ProjectPath, string.Empty);
|
||||
createdFileRelativePath = IOHelper.GetRelativePath(absPath);
|
||||
break;
|
||||
}
|
||||
else if (absPath != string.Empty)
|
||||
{
|
||||
var retry = EditorUtility.DisplayDialog("Invalid Path", "Please select the subdirectory of '" + Helper.ProjectPath + "'", "Retry", "Cancel");
|
||||
var retry = EditorUtility.DisplayDialog("Invalid Path", $"Please select the subdirectory of '{IOHelper.ProjectPath}'", "Retry", "Cancel");
|
||||
if (!retry) break;
|
||||
}
|
||||
else
|
||||
@ -403,7 +523,7 @@ namespace LWGUI
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!string.IsNullOrEmpty(createdFileRelativePath))
|
||||
{
|
||||
AssetDatabase.CreateAsset(rampAtlasSO, createdFileRelativePath);
|
||||
@ -411,8 +531,126 @@ namespace LWGUI
|
||||
rampAtlasSO.LoadTexture();
|
||||
return rampAtlasSO;
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Context Menu
|
||||
|
||||
public void ConvertColorSpace(ColorSpace targetColorSpace)
|
||||
{
|
||||
foreach (var ramp in Ramps)
|
||||
{
|
||||
if (ramp.ColorSpace != targetColorSpace)
|
||||
{
|
||||
ramp.ColorSpace = targetColorSpace;
|
||||
foreach (var gradient in ramp.GetGradients())
|
||||
{
|
||||
gradient?.ConvertColorSpaceWithoutCopy(
|
||||
targetColorSpace != ColorSpace.Gamma
|
||||
? ColorSpace.Linear
|
||||
: ColorSpace.Gamma);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rampAtlasSRGB = targetColorSpace == ColorSpace.Gamma;
|
||||
RampHelper.SetRampTextureImporter(_rampAtlasTexturePath, true, !rampAtlasSRGB, EditorJsonUtility.ToJson(this));
|
||||
UpdateTexturePixels();
|
||||
SaveTexture(checkoutAndForceWrite:true);
|
||||
}
|
||||
|
||||
[ContextMenu("Convert Gamma To Linear")]
|
||||
public void ConvertGammaToLinear()
|
||||
{
|
||||
ConvertColorSpace(ColorSpace.Linear);
|
||||
}
|
||||
|
||||
[ContextMenu("Convert Linear To Gamma")]
|
||||
public void ConvertLinearToGamma()
|
||||
{
|
||||
ConvertColorSpace(ColorSpace.Gamma);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Conversion Utilities
|
||||
|
||||
/// <summary>
|
||||
/// Convert an existing LwguiRampAtlas asset to a custom derived type.
|
||||
/// The new asset will be created at the same location with a suffix.
|
||||
/// For custom Ramp types with additional Gradients, the extra Gradients will be copied from the default Gradient.
|
||||
/// </summary>
|
||||
public static LwguiRampAtlas ConvertToType(LwguiRampAtlas source, Type targetType, bool saveToAsset = true, string suffix = "_Converted")
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
Debug.LogError("LWGUI: Source RampAtlas is null");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (targetType == null || !typeof(LwguiRampAtlas).IsAssignableFrom(targetType))
|
||||
{
|
||||
Debug.LogError($"LWGUI: Target type must be derived from LwguiRampAtlas");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create new instance of target type
|
||||
var newAtlas = ScriptableObject.CreateInstance(targetType) as LwguiRampAtlas;
|
||||
if (newAtlas == null)
|
||||
{
|
||||
Debug.LogError($"LWGUI: Failed to create instance of type '{targetType.Name}'");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Copy basic properties
|
||||
newAtlas.rampAtlasWidth = source.rampAtlasWidth;
|
||||
newAtlas.rampAtlasHeight = source.rampAtlasHeight;
|
||||
newAtlas.rampAtlasSRGB = source.rampAtlasSRGB;
|
||||
|
||||
// Convert each Ramp using CopyFrom interface
|
||||
foreach (var sourceRamp in source.Ramps)
|
||||
{
|
||||
if (sourceRamp == null) continue;
|
||||
|
||||
var newRamp = newAtlas.AddRamp();
|
||||
newRamp?.CopyFrom(sourceRamp);
|
||||
}
|
||||
|
||||
// Adjust height if needed
|
||||
while (newAtlas.TotalRowCount > newAtlas.rampAtlasHeight)
|
||||
{
|
||||
newAtlas.rampAtlasHeight *= 2;
|
||||
}
|
||||
|
||||
if (!saveToAsset)
|
||||
return newAtlas;
|
||||
|
||||
// Save as new asset
|
||||
var sourcePath = AssetDatabase.GetAssetPath(source);
|
||||
if (string.IsNullOrEmpty(sourcePath))
|
||||
{
|
||||
Debug.LogWarning("LWGUI: Source RampAtlas is not a saved asset");
|
||||
return newAtlas;
|
||||
}
|
||||
|
||||
var directory = Path.GetDirectoryName(sourcePath);
|
||||
var fileName = Path.GetFileNameWithoutExtension(sourcePath);
|
||||
var newPath = Path.Combine(directory, fileName + suffix + "." + RampAtlasSOExtensionName);
|
||||
newPath = AssetDatabase.GenerateUniqueAssetPath(newPath);
|
||||
|
||||
var result = SaveRampAtlasSOToAsset(newAtlas, directory, Path.GetFileNameWithoutExtension(newPath));
|
||||
|
||||
if (result != null)
|
||||
Debug.Log($"LWGUI: Successfully converted RampAtlas to '{targetType.Name}' at: {newPath}");
|
||||
else
|
||||
Debug.LogError($"LWGUI: Conversion of RampAtlas to '{targetType.Name}' failed at: {newPath}");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -283,18 +283,25 @@ namespace LWGUI
|
||||
if (presets == null)
|
||||
return null;
|
||||
|
||||
if (index < presets.Count)
|
||||
{
|
||||
if (index < presets.Count && index >= 0)
|
||||
return presets[index];
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"LWGUI: Index ({ index }) is out of range when accessing PresetFile: { name }");
|
||||
return null;
|
||||
}
|
||||
|
||||
Debug.LogError($"LWGUI: Index ({ index }) is out of range when accessing PresetFile: { name }");
|
||||
return null;
|
||||
}
|
||||
|
||||
public Preset GetPreset(float index) => GetPreset((int)index);
|
||||
public Preset TryGetPreset(int index)
|
||||
{
|
||||
if (presets == null)
|
||||
return null;
|
||||
|
||||
if (index < presets.Count && index >= 0)
|
||||
return presets[index];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Preset TryGetPreset(float floatIndex) => TryGetPreset(Mathf.CeilToInt(floatIndex));
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 799ea78c6ec34670ac45e915a967b2cc
|
||||
timeCreated: 1769096897
|
||||
@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 721b8d7aa96ce3243ad6e988775df5ee
|
||||
guid: 0087db252163ae340b69ec11fcd1e28a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
@ -0,0 +1,109 @@
|
||||
// Copyright (c) Jason Ma
|
||||
|
||||
using System;
|
||||
using LWGUI.Timeline;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace LWGUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a Folding Group
|
||||
///
|
||||
/// group: group name (Default: Property Name)
|
||||
/// keyword: keyword used for toggle, "_" = ignore, none or "__" = Property Name + "_ON", always Upper (Default: none)
|
||||
/// default Folding State: "on" or "off" (Default: off)
|
||||
/// default Toggle Displayed: "on" or "off" (Default: on)
|
||||
/// preset File Name: "Shader Property Preset" asset name, see Preset() for detail (Default: none)
|
||||
/// Target Property Type: Float, express Toggle value
|
||||
/// </summary>
|
||||
public class MainDrawer : MaterialPropertyDrawer, IBaseDrawer, IPresetDrawer
|
||||
{
|
||||
protected LWGUIMetaDatas metaDatas;
|
||||
|
||||
private static readonly float _height = 28f;
|
||||
|
||||
private bool _isFolding;
|
||||
private string _group;
|
||||
private string _keyword;
|
||||
private bool _defaultFoldingState;
|
||||
private bool _defaultToggleDisplayed;
|
||||
private string _presetFileName;
|
||||
|
||||
public MainDrawer() : this(String.Empty) { }
|
||||
|
||||
public MainDrawer(string group) : this(group, String.Empty) { }
|
||||
|
||||
public MainDrawer(string group, string keyword) : this(group, keyword, "off") { }
|
||||
|
||||
public MainDrawer(string group, string keyword, string defaultFoldingState) : this(group, keyword, defaultFoldingState, "on") { }
|
||||
|
||||
public MainDrawer(string group, string keyword, string defaultFoldingState, string defaultToggleDisplayed) : this(group, keyword, defaultFoldingState, defaultToggleDisplayed, String.Empty) { }
|
||||
|
||||
public MainDrawer(string group, string keyword, string defaultFoldingState, string defaultToggleDisplayed, string presetFileName)
|
||||
{
|
||||
this._group = group;
|
||||
this._keyword = keyword;
|
||||
this._defaultFoldingState = Helper.StringToBool(defaultFoldingState);
|
||||
this._defaultToggleDisplayed = Helper.StringToBool(defaultToggleDisplayed);
|
||||
this._presetFileName = presetFileName;
|
||||
}
|
||||
|
||||
public virtual void BuildStaticMetaData(Shader inShader, MaterialProperty inProp, MaterialProperty[] inProps, PropertyStaticData inoutPropertyStaticData)
|
||||
{
|
||||
inoutPropertyStaticData.groupName = _group;
|
||||
inoutPropertyStaticData.isMain = true;
|
||||
inoutPropertyStaticData.isExpanding = _defaultFoldingState;
|
||||
PerShaderData.DecodeMetaDataFromDisplayName(inProp, inoutPropertyStaticData);
|
||||
PresetDrawer.SetPresetAssetToStaticData(inoutPropertyStaticData, _presetFileName);
|
||||
}
|
||||
|
||||
public virtual void GetDefaultValueDescription(Shader inShader, MaterialProperty inProp, MaterialProperty inDefaultProp, PerShaderData inPerShaderData, PerMaterialData inoutPerMaterialData)
|
||||
{
|
||||
inoutPerMaterialData.propDynamicDatas[inProp.name].defaultValueDescription = inDefaultProp.floatValue > 0 ? "On" : "Off";
|
||||
}
|
||||
|
||||
public string GetPresetFileName() => _presetFileName;
|
||||
|
||||
public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
|
||||
{
|
||||
metaDatas = Helper.GetLWGUIMetadatas(editor);
|
||||
|
||||
var showMixedValue = EditorGUI.showMixedValue;
|
||||
EditorGUI.showMixedValue = prop.hasMixedValue;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
bool toggleResult = Helper.DrawFoldout(position, ref metaDatas.GetPropStaticData(prop).isExpanding, !Helper.Approximately(prop.floatValue, 0), _defaultToggleDisplayed, label);
|
||||
|
||||
if (Helper.EndChangeCheck(metaDatas, prop))
|
||||
{
|
||||
prop.floatValue = toggleResult ? 1.0f : 0.0f;
|
||||
var keyword = Helper.GetKeywordName(_keyword, prop.name);
|
||||
Helper.SetShaderKeywordEnabled(editor.targets, keyword, toggleResult);
|
||||
PresetHelper.GetPresetAsset(_presetFileName)?.TryGetPreset(prop.floatValue)?.ApplyToEditingMaterial(editor, metaDatas.perMaterialData);
|
||||
TimelineHelper.SetKeywordToggleToTimeline(prop, editor, keyword);
|
||||
}
|
||||
EditorGUI.showMixedValue = showMixedValue;
|
||||
}
|
||||
|
||||
// Call in custom shader gui
|
||||
public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
|
||||
{
|
||||
return _height;
|
||||
}
|
||||
|
||||
// Call when create/edit/undo materials.
|
||||
// Used to set material settings such as Keywords that need to be kept synchronized with the value forever.
|
||||
// DO NOT modify other properties here!!! Otherwise, manually modified values will be overwritten.
|
||||
public override void Apply(MaterialProperty prop)
|
||||
{
|
||||
base.Apply(prop);
|
||||
if (!prop.hasMixedValue && VersionControlHelper.IsWriteable(prop.targets))
|
||||
{
|
||||
Helper.SetShaderKeywordEnabled(prop.targets, Helper.GetKeywordName(_keyword, prop.name), prop.floatValue > 0f);
|
||||
PresetDrawer.ApplyPresetWithoutPropertyChanges(_presetFileName, prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 80bd09bde334c2c439f7374bb2ba821c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,74 @@
|
||||
// Copyright (c) Jason Ma
|
||||
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace LWGUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Draw a property with default style in the folding group
|
||||
///
|
||||
/// group: parent group name (Default: none)
|
||||
/// Target Property Type: Any
|
||||
/// </summary>
|
||||
public class SubDrawer : MaterialPropertyDrawer, IBaseDrawer
|
||||
{
|
||||
public string group = String.Empty;
|
||||
public LWGUIMetaDatas metaDatas;
|
||||
|
||||
public SubDrawer() { }
|
||||
|
||||
public SubDrawer(string group)
|
||||
{
|
||||
this.group = group;
|
||||
}
|
||||
|
||||
protected virtual bool IsMatchPropType(MaterialProperty property) { return true; }
|
||||
|
||||
protected virtual float GetVisibleHeight(MaterialProperty prop)
|
||||
{
|
||||
var height = MaterialEditor.GetDefaultPropertyHeight(prop);
|
||||
return prop.GetPropertyType() == ShaderPropertyType.Vector ? EditorGUIUtility.singleLineHeight : height;
|
||||
}
|
||||
|
||||
public virtual void BuildStaticMetaData(Shader inShader, MaterialProperty inProp, MaterialProperty[] inProps, PropertyStaticData inoutPropertyStaticData)
|
||||
{
|
||||
inoutPropertyStaticData.groupName = group;
|
||||
PerShaderData.DecodeMetaDataFromDisplayName(inProp, inoutPropertyStaticData);
|
||||
}
|
||||
|
||||
public virtual void GetDefaultValueDescription(Shader inShader, MaterialProperty inProp, MaterialProperty inDefaultProp, PerShaderData inPerShaderData, PerMaterialData inoutPerMaterialData) { }
|
||||
|
||||
public virtual void GetCustomContextMenus(GenericMenu menu, Rect rect, MaterialProperty prop, LWGUIMetaDatas metaDatas) { }
|
||||
|
||||
public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
|
||||
{
|
||||
metaDatas = Helper.GetLWGUIMetadatas(editor);
|
||||
|
||||
if (IsMatchPropType(prop))
|
||||
{
|
||||
DrawProp(position, prop, label, editor);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("LWGUI: Property:'" + prop.name + "' Type:'" + prop.GetPropertyType() + "' mismatch!");
|
||||
editor.DefaultShaderProperty(position, prop, label.text);
|
||||
}
|
||||
}
|
||||
|
||||
public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor)
|
||||
{
|
||||
return GetVisibleHeight(prop);
|
||||
}
|
||||
|
||||
// Draws a custom style property
|
||||
public virtual void DrawProp(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
|
||||
{
|
||||
RevertableHelper.FixGUIWidthMismatch(prop.GetPropertyType(), editor);
|
||||
editor.DefaultShaderPropertyInternal(position, prop, label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1018092e809fcf448a004c21f19c56cb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -1,6 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 734309d3fd8d31246bde14f3278d4ee3
|
||||
ShaderIncludeImporter:
|
||||
guid: d88e9b9124470694db30ecb3c7da26c2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e5aaa8816648ca4eaed701110c637d4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,41 @@
|
||||
// Copyright (c) Jason Ma
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace LWGUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Display a Helpbox on the property
|
||||
/// You can also use "%Text" in DisplayName to add Helpbox that supports Multi-Language.
|
||||
///
|
||||
/// message: a single-line string to display, support up to 4 ','. (Default: Newline)
|
||||
/// </summary>
|
||||
public class HelpboxDecorator : TooltipDecorator
|
||||
{
|
||||
private string _message;
|
||||
|
||||
|
||||
#region
|
||||
|
||||
public HelpboxDecorator() : this(string.Empty) { }
|
||||
|
||||
public HelpboxDecorator(string message) { this._message = message; }
|
||||
|
||||
public HelpboxDecorator(string s1, string s2) : this(s1 + ", " + s2) { }
|
||||
|
||||
public HelpboxDecorator(string s1, string s2, string s3) : this(s1 + ", " + s2 + ", " + s3) { }
|
||||
|
||||
public HelpboxDecorator(string s1, string s2, string s3, string s4) : this(s1 + ", " + s2 + ", " + s3 + ", " + s4) { }
|
||||
|
||||
public HelpboxDecorator(string s1, string s2, string s3, string s4, string s5) : this(s1 + ", " + s2 + ", " + s3 + ", " + s4 + ", " + s5) { }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
public override void BuildStaticMetaData(Shader inShader, MaterialProperty inProp, MaterialProperty[] inProps, PropertyStaticData inoutPropertyStaticData)
|
||||
{
|
||||
inoutPropertyStaticData.helpboxMessages += _message + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c60e1921fd5b285489b55fa3650c40a8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,22 @@
|
||||
// Copyright (c) Jason Ma
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace LWGUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Set the property to read-only.
|
||||
/// </summary>
|
||||
public class ReadOnlyDecorator : SubDrawer
|
||||
{
|
||||
protected override float GetVisibleHeight(MaterialProperty prop) { return 0; }
|
||||
|
||||
public override void BuildStaticMetaData(Shader inShader, MaterialProperty inProp, MaterialProperty[] inProps, PropertyStaticData inoutPropertyStaticData)
|
||||
{
|
||||
inoutPropertyStaticData.isReadOnly = true;
|
||||
}
|
||||
|
||||
public override void DrawProp(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor) { }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e86854765f20f184185f804b18f4bd1c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,18 @@
|
||||
// Copyright (c) Jason Ma
|
||||
|
||||
namespace LWGUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Similar to Title()
|
||||
///
|
||||
/// group: parent group name (Default: none)
|
||||
/// header: string to display, "SpaceLine" or "_" = none (Default: none)
|
||||
/// height: line height (Default: 22)
|
||||
/// </summary>
|
||||
public class SubTitleDecorator : TitleDecorator
|
||||
{
|
||||
public SubTitleDecorator(string group, string header) : base(group, header, DefaultHeight) { }
|
||||
|
||||
public SubTitleDecorator(string group, string header, float height) : base(group, header, height) { }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 59dc6bb7a17b4da4c9ea5d75aed10b7e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,46 @@
|
||||
// Copyright (c) Jason Ma
|
||||
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace LWGUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Similar to Header()
|
||||
///
|
||||
/// group: parent group name (Default: none)
|
||||
/// header: string to display, "SpaceLine" or "_" = none (Default: none)
|
||||
/// height: line height (Default: 22)
|
||||
/// </summary>
|
||||
public class TitleDecorator : SubDrawer
|
||||
{
|
||||
private string _header;
|
||||
private float _height;
|
||||
|
||||
public static readonly float DefaultHeight = EditorGUIUtility.singleLineHeight + 6f;
|
||||
|
||||
protected override float GetVisibleHeight(MaterialProperty prop) { return _height; }
|
||||
|
||||
public override void BuildStaticMetaData(Shader inShader, MaterialProperty inProp, MaterialProperty[] inProps, PropertyStaticData inoutPropertyStaticData) { }
|
||||
|
||||
public TitleDecorator(string header) : this("_", header, DefaultHeight) { }
|
||||
|
||||
public TitleDecorator(string header, float height) : this("_", header, height) { }
|
||||
|
||||
public TitleDecorator(string group, string header) : this(group, header, DefaultHeight) { }
|
||||
|
||||
public TitleDecorator(string group, string header, float height)
|
||||
{
|
||||
this.group = group;
|
||||
this._header = header == "SpaceLine" || header == "_" ? String.Empty : header;
|
||||
this._height = height;
|
||||
}
|
||||
|
||||
public override void DrawProp(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
|
||||
{
|
||||
position = EditorGUI.IndentedRect(position);
|
||||
GUI.Label(position, _header, GUIStyles.title);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 09b3118ef6d65ec49bdd423f20f09f09
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,45 @@
|
||||
// Copyright (c) Jason Ma
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace LWGUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Tooltip, describes the details of the property. (Default: property.name and property default value)
|
||||
/// You can also use "#Text" in DisplayName to add Tooltip that supports Multi-Language.
|
||||
///
|
||||
/// tooltip: a single-line string to display, support up to 4 ','. (Default: Newline)
|
||||
/// </summary>
|
||||
public class TooltipDecorator : SubDrawer
|
||||
{
|
||||
private string _tooltip;
|
||||
|
||||
|
||||
#region
|
||||
|
||||
public TooltipDecorator() : this(string.Empty) { }
|
||||
|
||||
public TooltipDecorator(string tooltip) { this._tooltip = tooltip; }
|
||||
|
||||
public TooltipDecorator(string s1, string s2) : this(s1 + ", " + s2) { }
|
||||
|
||||
public TooltipDecorator(string s1, string s2, string s3) : this(s1 + ", " + s2 + ", " + s3) { }
|
||||
|
||||
public TooltipDecorator(string s1, string s2, string s3, string s4) : this(s1 + ", " + s2 + ", " + s3 + ", " + s4) { }
|
||||
|
||||
public TooltipDecorator(string s1, string s2, string s3, string s4, string s5) : this(s1 + ", " + s2 + ", " + s3 + ", " + s4 + ", " + s5) { }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
protected override float GetVisibleHeight(MaterialProperty prop) { return 0; }
|
||||
|
||||
public override void BuildStaticMetaData(Shader inShader, MaterialProperty inProp, MaterialProperty[] inProps, PropertyStaticData inoutPropertyStaticData)
|
||||
{
|
||||
inoutPropertyStaticData.tooltipMessages += _tooltip + "\n";
|
||||
}
|
||||
|
||||
public override void DrawProp(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor) { }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5934a4eb43f5b3b48b5396a2cb653f76
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 921ffaf8b8533bf42983cf1189a4ad45
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,39 @@
|
||||
// Copyright (c) Jason Ma
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace LWGUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Control whether the property can be edited based on multiple conditions.
|
||||
///
|
||||
/// logicalOperator: And | Or (Default: And).
|
||||
/// propName: Target Property Name used for comparison.
|
||||
/// compareFunction: Less (L) | Equal (E) | LessEqual (LEqual / LE) | Greater (G) | NotEqual (NEqual / NE) | GreaterEqual (GEqual / GE).
|
||||
/// value: Target Property Value used for comparison.
|
||||
/// </summary>
|
||||
public class ActiveIfDecorator : SubDrawer
|
||||
{
|
||||
public ShowIfDecorator.ShowIfData activeIfData = new();
|
||||
|
||||
public ActiveIfDecorator(string propName, string comparisonMethod, float value) : this("And", propName, comparisonMethod, value) { }
|
||||
|
||||
public ActiveIfDecorator(string logicalOperator, string propName, string compareFunction, float value)
|
||||
{
|
||||
activeIfData.logicalOperator = logicalOperator.ToLower() == "or" ? ShowIfDecorator.LogicalOperator.Or : ShowIfDecorator.LogicalOperator.And;
|
||||
activeIfData.targetPropertyName = propName;
|
||||
activeIfData.compareFunction = ShowIfDecorator.ParseCompareFunction(compareFunction);
|
||||
activeIfData.value = value;
|
||||
}
|
||||
|
||||
protected override float GetVisibleHeight(MaterialProperty prop) { return 0; }
|
||||
|
||||
public override void BuildStaticMetaData(Shader inShader, MaterialProperty inProp, MaterialProperty[] inProps, PropertyStaticData inoutPropertyStaticData)
|
||||
{
|
||||
inoutPropertyStaticData.activeIfDatas.Add(activeIfData);
|
||||
}
|
||||
|
||||
public override void DrawProp(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor) { }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 224088b56f7a2814ba7520292da26cf8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,22 @@
|
||||
// Copyright (c) Jason Ma
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace LWGUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Similar to HideInInspector(), the difference is that Hidden() can be unhidden through the Display Mode button.
|
||||
/// </summary>
|
||||
public class HiddenDecorator : SubDrawer
|
||||
{
|
||||
protected override float GetVisibleHeight(MaterialProperty prop) { return 0; }
|
||||
|
||||
public override void BuildStaticMetaData(Shader inShader, MaterialProperty inProp, MaterialProperty[] inProps, PropertyStaticData inoutPropertyStaticData)
|
||||
{
|
||||
inoutPropertyStaticData.isHidden = true;
|
||||
}
|
||||
|
||||
public override void DrawProp(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor) { }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b730963b9bced7f418f78f2502b39607
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,170 @@
|
||||
// Copyright (c) Jason Ma
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace LWGUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Control the show or hide of a single or a group of properties based on multiple conditions.
|
||||
///
|
||||
/// logicalOperator: And | Or (Default: And).
|
||||
/// propName: Target Property Name used for comparison.
|
||||
/// compareFunction: Less (L) | Equal (E) | LessEqual (LEqual / LE) | Greater (G) | NotEqual (NEqual / NE) | GreaterEqual (GEqual / GE).
|
||||
/// value: Target Property Value used for comparison.
|
||||
/// </summary>
|
||||
public class ShowIfDecorator : SubDrawer
|
||||
{
|
||||
public enum LogicalOperator
|
||||
{
|
||||
And,
|
||||
Or
|
||||
}
|
||||
|
||||
public class ShowIfData
|
||||
{
|
||||
public LogicalOperator logicalOperator = LogicalOperator.And;
|
||||
public string targetPropertyName = string.Empty;
|
||||
public CompareFunction compareFunction = CompareFunction.Equal;
|
||||
public float value = 0;
|
||||
}
|
||||
|
||||
public ShowIfData showIfData = new();
|
||||
|
||||
private static readonly Dictionary<string, string> _compareFunctionLUT = new()
|
||||
{
|
||||
{ "Less", "Less" },
|
||||
{ "L", "Less" },
|
||||
{ "Equal", "Equal" },
|
||||
{ "E", "Equal" },
|
||||
{ "LessEqual", "LessEqual" },
|
||||
{ "LEqual", "LessEqual" },
|
||||
{ "LE", "LessEqual" },
|
||||
{ "Greater", "Greater" },
|
||||
{ "G", "Greater" },
|
||||
{ "NotEqual", "NotEqual" },
|
||||
{ "NEqual", "NotEqual" },
|
||||
{ "NE", "NotEqual" },
|
||||
{ "GreaterEqual", "GreaterEqual" },
|
||||
{ "GEqual", "GreaterEqual" },
|
||||
{ "GE", "GreaterEqual" },
|
||||
};
|
||||
|
||||
public static CompareFunction ParseCompareFunction(string compareFunction)
|
||||
{
|
||||
if (!_compareFunctionLUT.TryGetValue(compareFunction, out var compareFunctionName)
|
||||
|| !Enum.IsDefined(typeof(CompareFunction), compareFunctionName))
|
||||
{
|
||||
Debug.LogError("LWGUI: Invalid compareFunction: '"
|
||||
+ compareFunction
|
||||
+ "', Must be one of the following: Less (L) | Equal (E) | LessEqual (LEqual / LE) | Greater (G) | NotEqual (NEqual / NE) | GreaterEqual (GEqual / GE).");
|
||||
return CompareFunction.Equal;
|
||||
}
|
||||
|
||||
return (CompareFunction)Enum.Parse(typeof(CompareFunction), compareFunctionName);
|
||||
}
|
||||
|
||||
public ShowIfDecorator(string propName, string comparisonMethod, float value) : this("And", propName, comparisonMethod, value) { }
|
||||
|
||||
public ShowIfDecorator(string logicalOperator, string propName, string compareFunction, float value)
|
||||
{
|
||||
showIfData.logicalOperator = logicalOperator.ToLower() == "or" ? LogicalOperator.Or : LogicalOperator.And;
|
||||
showIfData.targetPropertyName = propName;
|
||||
showIfData.compareFunction = ParseCompareFunction(compareFunction);
|
||||
showIfData.value = value;
|
||||
}
|
||||
|
||||
private static void Compare(ShowIfData showIfData, float targetValue, ref bool result)
|
||||
{
|
||||
bool compareResult;
|
||||
|
||||
switch (showIfData.compareFunction)
|
||||
{
|
||||
case CompareFunction.Less:
|
||||
compareResult = targetValue < showIfData.value;
|
||||
break;
|
||||
case CompareFunction.LessEqual:
|
||||
compareResult = targetValue <= showIfData.value;
|
||||
break;
|
||||
case CompareFunction.Greater:
|
||||
compareResult = targetValue > showIfData.value;
|
||||
break;
|
||||
case CompareFunction.NotEqual:
|
||||
compareResult = targetValue != showIfData.value;
|
||||
break;
|
||||
case CompareFunction.GreaterEqual:
|
||||
compareResult = targetValue >= showIfData.value;
|
||||
break;
|
||||
default:
|
||||
compareResult = targetValue == showIfData.value;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (showIfData.logicalOperator)
|
||||
{
|
||||
case LogicalOperator.And:
|
||||
result &= compareResult;
|
||||
break;
|
||||
case LogicalOperator.Or:
|
||||
result |= compareResult;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool GetShowIfResultToFilterDrawerApplying(MaterialProperty prop)
|
||||
{
|
||||
var material = prop.targets[0] as Material;
|
||||
var showIfDatas = new List<ShowIfData>();
|
||||
{
|
||||
var drawer = ReflectionHelper.GetPropertyDrawer(material.shader, prop, out var decoratorDrawers);
|
||||
if (decoratorDrawers != null && decoratorDrawers.Count > 0)
|
||||
{
|
||||
foreach (ShowIfDecorator showIfDecorator in decoratorDrawers.Where(drawer => drawer is ShowIfDecorator))
|
||||
{
|
||||
showIfDatas.Add(showIfDecorator.showIfData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return GetShowIfResultFromMaterial(showIfDatas, material);
|
||||
}
|
||||
|
||||
public static bool GetShowIfResultFromMaterial(List<ShowIfData> showIfDatas, Material material)
|
||||
{
|
||||
bool result = true;
|
||||
foreach (var showIfData in showIfDatas)
|
||||
{
|
||||
var targetValue = material.GetFloat(showIfData.targetPropertyName);
|
||||
Compare(showIfData, targetValue, ref result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void GetShowIfResult(PropertyStaticData propStaticData, PropertyDynamicData propDynamicData, PerMaterialData perMaterialData)
|
||||
{
|
||||
foreach (var showIfData in propStaticData.showIfDatas)
|
||||
{
|
||||
var targetValue = perMaterialData.propDynamicDatas[showIfData.targetPropertyName].property.floatValue;
|
||||
Compare(showIfData, targetValue, ref propDynamicData.isShowing);
|
||||
}
|
||||
}
|
||||
|
||||
protected override float GetVisibleHeight(MaterialProperty prop) { return 0; }
|
||||
|
||||
public override void BuildStaticMetaData(Shader inShader, MaterialProperty inProp, MaterialProperty[] inProps, PropertyStaticData inoutPropertyStaticData)
|
||||
{
|
||||
inoutPropertyStaticData.showIfDatas.Add(showIfData);
|
||||
}
|
||||
|
||||
public override void DrawProp(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor) { }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f391774ca908ee42abfa5b95dea3b8f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 39a1eac873eb3a04a8bd4ad4805745b9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,71 @@
|
||||
// Copyright (c) Jason Ma
|
||||
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace LWGUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Cooperate with Toggle to switch certain Passes.
|
||||
///
|
||||
/// lightModeName(s): Light Mode in Shader Pass (https://docs.unity3d.com/2017.4/Documentation/Manual/SL-PassTags.html)
|
||||
/// </summary>
|
||||
public class PassSwitchDecorator : SubDrawer
|
||||
{
|
||||
private string[] _lightModeNames;
|
||||
|
||||
|
||||
#region
|
||||
|
||||
public PassSwitchDecorator(string lightModeName1)
|
||||
: this(new[] { lightModeName1 }) { }
|
||||
|
||||
public PassSwitchDecorator(string lightModeName1, string lightModeName2)
|
||||
: this(new[] { lightModeName1, lightModeName2 }) { }
|
||||
|
||||
public PassSwitchDecorator(string lightModeName1, string lightModeName2, string lightModeName3)
|
||||
: this(new[] { lightModeName1, lightModeName2, lightModeName3 }) { }
|
||||
|
||||
public PassSwitchDecorator(string lightModeName1, string lightModeName2, string lightModeName3, string lightModeName4)
|
||||
: this(new[] { lightModeName1, lightModeName2, lightModeName3, lightModeName4 }) { }
|
||||
|
||||
public PassSwitchDecorator(string lightModeName1, string lightModeName2, string lightModeName3, string lightModeName4, string lightModeName5)
|
||||
: this(new[] { lightModeName1, lightModeName2, lightModeName3, lightModeName4, lightModeName5 }) { }
|
||||
|
||||
public PassSwitchDecorator(string lightModeName1, string lightModeName2, string lightModeName3, string lightModeName4, string lightModeName5, string lightModeName6)
|
||||
: this(new[] { lightModeName1, lightModeName2, lightModeName3, lightModeName4, lightModeName5, lightModeName6 }) { }
|
||||
|
||||
public PassSwitchDecorator(string[] lightModeNames) { _lightModeNames = lightModeNames.Select((s => s.ToUpper())).ToArray(); }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
protected override float GetVisibleHeight(MaterialProperty prop) { return 0; }
|
||||
|
||||
protected override bool IsMatchPropType(MaterialProperty property)
|
||||
{
|
||||
return property.GetPropertyType() == ShaderPropertyType.Float
|
||||
|| property.GetPropertyType() == ShaderPropertyType.Int;
|
||||
}
|
||||
|
||||
public override void BuildStaticMetaData(Shader inShader, MaterialProperty inProp, MaterialProperty[] inProps, PropertyStaticData inoutPropertyStaticData) { }
|
||||
|
||||
public override void DrawProp(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
|
||||
{
|
||||
if (!prop.hasMixedValue && VersionControlHelper.IsWriteable(prop.targets))
|
||||
Helper.SetShaderPassEnabled(prop.targets, _lightModeNames, prop.floatValue > 0);
|
||||
}
|
||||
|
||||
public override void Apply(MaterialProperty prop)
|
||||
{
|
||||
base.Apply(prop);
|
||||
if (!prop.hasMixedValue && VersionControlHelper.IsWriteable(prop.targets))
|
||||
{
|
||||
if (ShowIfDecorator.GetShowIfResultToFilterDrawerApplying(prop))
|
||||
Helper.SetShaderPassEnabled(prop.targets, _lightModeNames, prop.floatValue > 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8ce1e6b3dcc10bf4d904506b5427dd62
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a3d4d8d42a6cdcf4280e9f094d31280c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,36 @@
|
||||
// Copyright (c) Jason Ma
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace LWGUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Collapse the current Property into an Advanced Block.
|
||||
/// Specify the Header String to create a new Advanced Block.
|
||||
/// All Properties using Advanced() will be collapsed into the nearest Advanced Block.
|
||||
///
|
||||
/// headerString: The title of the Advanced Block. Default: "Advanced"
|
||||
/// </summary>
|
||||
public class AdvancedDecorator : SubDrawer
|
||||
{
|
||||
private string headerString;
|
||||
|
||||
public AdvancedDecorator() : this(string.Empty) { }
|
||||
|
||||
public AdvancedDecorator(string headerString)
|
||||
{
|
||||
this.headerString = headerString;
|
||||
}
|
||||
|
||||
protected override float GetVisibleHeight(MaterialProperty prop) { return 0; }
|
||||
|
||||
public override void BuildStaticMetaData(Shader inShader, MaterialProperty inProp, MaterialProperty[] inProps, PropertyStaticData inoutPropertyStaticData)
|
||||
{
|
||||
inoutPropertyStaticData.isAdvanced = true;
|
||||
inoutPropertyStaticData.advancedHeaderString = headerString;
|
||||
}
|
||||
|
||||
public override void DrawProp(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor) { }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f94593a3791d98342bcf7e2ce8a434ac
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,24 @@
|
||||
// Copyright (c) Jason Ma
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace LWGUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Create an Advanced Block using the current Property as the Header.
|
||||
/// </summary>
|
||||
public class AdvancedHeaderPropertyDecorator : SubDrawer
|
||||
{
|
||||
protected override float GetVisibleHeight(MaterialProperty prop) { return 0; }
|
||||
|
||||
public override void BuildStaticMetaData(Shader inShader, MaterialProperty inProp, MaterialProperty[] inProps, PropertyStaticData inoutPropertyStaticData)
|
||||
{
|
||||
inoutPropertyStaticData.isAdvanced = true;
|
||||
inoutPropertyStaticData.isAdvancedHeader = true;
|
||||
inoutPropertyStaticData.isAdvancedHeaderProperty = true;
|
||||
}
|
||||
|
||||
public override void DrawProp(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor) { }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eda92ad2828dbbf4b8ad1ceec6a203bd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f91db697c82843841ac35226be10a332
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user