NB_FX
This commit is contained in:
8
Packages/NB_FX/NBShaders2/Editor.meta
Normal file
8
Packages/NB_FX/NBShaders2/Editor.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 95e40beb3a78e8144b5af938f9e6b2e6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Packages/NB_FX/NBShaders2/Editor/FeatureLevel.meta
Normal file
8
Packages/NB_FX/NBShaders2/Editor/FeatureLevel.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fb8ba48b3d36342e3bd039e124cae415
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a87eea7424634acb8293156f600ae92a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 78855065a5a548f490982968a0e01781
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,232 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NBShader;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBShaders2.Editor.FeatureLevel
|
||||
{
|
||||
[InitializeOnLoad]
|
||||
internal static class NBShaderEditorQualityTierWatcher
|
||||
{
|
||||
private const string UndoApplyLoadedTier = "Apply NBShader Quality Tier";
|
||||
private const string UndoApplyProjectTier = "Apply NBShader Quality Tier To Project Materials";
|
||||
|
||||
private static int s_LastQualityLevel;
|
||||
private static bool s_DialogQueued;
|
||||
|
||||
static NBShaderEditorQualityTierWatcher()
|
||||
{
|
||||
s_LastQualityLevel = QualitySettings.GetQualityLevel();
|
||||
EditorApplication.update += WatchQualityLevel;
|
||||
}
|
||||
|
||||
internal static void ApplyCurrentQualityTierToLoadedMaterials()
|
||||
{
|
||||
NBShaderFeatureTier tier;
|
||||
string qualityName;
|
||||
ResolveCurrentQualityTier(out tier, out qualityName);
|
||||
var count = ApplyTierToLoadedMaterials(tier);
|
||||
Debug.LogFormat(
|
||||
"Applied NBShader2 tier {0} from Unity Quality '{1}' to {2} loaded material(s).",
|
||||
tier,
|
||||
qualityName,
|
||||
count);
|
||||
}
|
||||
|
||||
internal static void ApplyCurrentQualityTierToProjectMaterials()
|
||||
{
|
||||
NBShaderFeatureTier tier;
|
||||
string qualityName;
|
||||
ResolveCurrentQualityTier(out tier, out qualityName);
|
||||
var count = ApplyTierToProjectMaterials(tier);
|
||||
Debug.LogFormat(
|
||||
"Applied NBShader2 tier {0} from Unity Quality '{1}' to {2} project material asset(s).",
|
||||
tier,
|
||||
qualityName,
|
||||
count);
|
||||
}
|
||||
|
||||
private static void WatchQualityLevel()
|
||||
{
|
||||
if (Application.isBatchMode ||
|
||||
EditorApplication.isCompiling ||
|
||||
EditorApplication.isUpdating ||
|
||||
EditorApplication.isPlayingOrWillChangePlaymode ||
|
||||
s_DialogQueued)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var current = QualitySettings.GetQualityLevel();
|
||||
if (current == s_LastQualityLevel)
|
||||
return;
|
||||
|
||||
s_LastQualityLevel = current;
|
||||
s_DialogQueued = true;
|
||||
EditorApplication.delayCall += PromptForQualityTierSync;
|
||||
}
|
||||
|
||||
private static void PromptForQualityTierSync()
|
||||
{
|
||||
s_DialogQueued = false;
|
||||
if (Application.isBatchMode ||
|
||||
EditorApplication.isCompiling ||
|
||||
EditorApplication.isUpdating ||
|
||||
EditorApplication.isPlayingOrWillChangePlaymode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NBShaderFeatureTier tier;
|
||||
string qualityName;
|
||||
ResolveCurrentQualityTier(out tier, out qualityName);
|
||||
|
||||
var syncLoaded = EditorUtility.DisplayDialog(
|
||||
"NBShader2 Feature Tier",
|
||||
string.Format(
|
||||
"Unity Quality has switched to '{0}'.\n\nSync currently loaded NBShader2 materials to tier {1}?",
|
||||
qualityName,
|
||||
tier),
|
||||
"Sync Loaded",
|
||||
"Skip");
|
||||
if (!syncLoaded)
|
||||
return;
|
||||
|
||||
var loadedCount = ApplyTierToLoadedMaterials(tier);
|
||||
|
||||
var syncProject = EditorUtility.DisplayDialog(
|
||||
"NBShader2 Feature Tier",
|
||||
string.Format(
|
||||
"Applied tier {0} to {1} loaded NBShader2 material(s).\n\nScan Assets and write current keyword/pass state for all NBShader2 material assets?",
|
||||
tier,
|
||||
loadedCount),
|
||||
"Scan Assets",
|
||||
"Loaded Only");
|
||||
if (syncProject)
|
||||
{
|
||||
var projectCount = ApplyTierToProjectMaterials(tier);
|
||||
Debug.LogFormat(
|
||||
"Applied NBShader2 tier {0} from Unity Quality '{1}' to {2} project material asset(s).",
|
||||
tier,
|
||||
qualityName,
|
||||
projectCount);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ResolveCurrentQualityTier(out NBShaderFeatureTier tier, out string qualityName)
|
||||
{
|
||||
qualityName = GetCurrentQualityName();
|
||||
if (!NBShaderFeatureLevelProjectSettings.instance.TryGetTierForQualityNameNoSave(qualityName, out tier))
|
||||
tier = NBShaderFeatureTier.Ultra;
|
||||
}
|
||||
|
||||
private static string GetCurrentQualityName()
|
||||
{
|
||||
var names = QualitySettings.names;
|
||||
var index = QualitySettings.GetQualityLevel();
|
||||
if (names == null || index < 0 || index >= names.Length)
|
||||
return string.Empty;
|
||||
return names[index];
|
||||
}
|
||||
|
||||
private static int ApplyTierToLoadedMaterials(NBShaderFeatureTier tier)
|
||||
{
|
||||
var materials = Resources.FindObjectsOfTypeAll<Material>();
|
||||
var editableMaterials = new List<Material>();
|
||||
var seen = new HashSet<int>();
|
||||
for (var i = 0; i < materials.Length; i++)
|
||||
{
|
||||
var material = materials[i];
|
||||
if (!CanMutateMaterial(material) || !seen.Add(material.GetInstanceID()))
|
||||
continue;
|
||||
editableMaterials.Add(material);
|
||||
}
|
||||
|
||||
if (editableMaterials.Count == 0)
|
||||
return 0;
|
||||
|
||||
var undoGroup = Undo.GetCurrentGroup();
|
||||
Undo.SetCurrentGroupName(UndoApplyLoadedTier);
|
||||
for (var i = 0; i < editableMaterials.Count; i++)
|
||||
ApplyTierToMaterial(editableMaterials[i], tier, UndoApplyLoadedTier);
|
||||
Undo.CollapseUndoOperations(undoGroup);
|
||||
return editableMaterials.Count;
|
||||
}
|
||||
|
||||
private static int ApplyTierToProjectMaterials(NBShaderFeatureTier tier)
|
||||
{
|
||||
var guids = AssetDatabase.FindAssets("t:Material", new[] { "Assets" });
|
||||
var changed = 0;
|
||||
var undoGroup = Undo.GetCurrentGroup();
|
||||
Undo.SetCurrentGroupName(UndoApplyProjectTier);
|
||||
|
||||
try
|
||||
{
|
||||
for (var i = 0; i < guids.Length; i++)
|
||||
{
|
||||
if (EditorUtility.DisplayCancelableProgressBar(
|
||||
"NBShader2 Feature Tier",
|
||||
string.Format("Scanning material {0}/{1}", i + 1, guids.Length),
|
||||
guids.Length > 0 ? (float)i / guids.Length : 1f))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var path = AssetDatabase.GUIDToAssetPath(guids[i]);
|
||||
var material = AssetDatabase.LoadAssetAtPath<Material>(path);
|
||||
if (!CanMutateMaterial(material))
|
||||
continue;
|
||||
|
||||
if (ApplyTierToMaterial(material, tier, UndoApplyProjectTier))
|
||||
{
|
||||
AssetDatabase.SaveAssetIfDirty(material);
|
||||
changed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
Undo.CollapseUndoOperations(undoGroup);
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
private static bool CanMutateMaterial(Material material)
|
||||
{
|
||||
if (!NBShaderMaterialIntentResolver.IsNBShaderMaterial(material))
|
||||
return false;
|
||||
|
||||
var flags = material.hideFlags;
|
||||
if ((flags & HideFlags.NotEditable) != 0 ||
|
||||
(flags & HideFlags.HideAndDontSave) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!EditorUtility.IsPersistent(material))
|
||||
return true;
|
||||
|
||||
var path = AssetDatabase.GetAssetPath(material);
|
||||
return !string.IsNullOrEmpty(path) && path.StartsWith("Assets/", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private static bool ApplyTierToMaterial(
|
||||
Material material,
|
||||
NBShaderFeatureTier tier,
|
||||
string undoName)
|
||||
{
|
||||
Undo.RecordObject(material, undoName);
|
||||
bool changed;
|
||||
if (!NBShaderFeatureLevelMaterialApplier.Apply(material, tier, true, true, out changed))
|
||||
return false;
|
||||
|
||||
if (changed)
|
||||
EditorUtility.SetDirty(material);
|
||||
|
||||
return changed;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 48ae002b228442d4a26f69f91cef7810
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,749 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NBShader;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace NBShaders2.Editor.FeatureLevel
|
||||
{
|
||||
public enum NBShaderBuildInfoMode
|
||||
{
|
||||
TierAndPass = 0,
|
||||
ExactMaterialVariants = 1
|
||||
}
|
||||
|
||||
public sealed class NBShaderBuildInfoSet
|
||||
{
|
||||
private readonly NBShaderMaterialBuildInfo[] m_Materials;
|
||||
private readonly NBShaderVariantBuildInfo[] m_Variants;
|
||||
private readonly string[] m_AllowedManagedKeywords;
|
||||
private readonly string[] m_AllowedPassFeatures;
|
||||
private readonly string[] m_IncludedPassNames;
|
||||
|
||||
public readonly NBShaderFeatureTier tier;
|
||||
public readonly NBShaderBuildInfoMode mode;
|
||||
|
||||
public NBShaderMaterialBuildInfo[] materials { get { return (NBShaderMaterialBuildInfo[])m_Materials.Clone(); } }
|
||||
public NBShaderVariantBuildInfo[] variants { get { return (NBShaderVariantBuildInfo[])m_Variants.Clone(); } }
|
||||
public string[] allowedManagedKeywords { get { return (string[])m_AllowedManagedKeywords.Clone(); } }
|
||||
public string[] allowedPassFeatures { get { return (string[])m_AllowedPassFeatures.Clone(); } }
|
||||
public string[] includedPassNames { get { return (string[])m_IncludedPassNames.Clone(); } }
|
||||
public bool hasMaterials { get { return m_Materials.Length > 0; } }
|
||||
|
||||
public NBShaderBuildInfoSet(
|
||||
NBShaderFeatureTier tier,
|
||||
NBShaderBuildInfoMode mode,
|
||||
NBShaderMaterialBuildInfo[] materials,
|
||||
string[] allowedManagedKeywords,
|
||||
string[] allowedPassFeatures = null)
|
||||
{
|
||||
this.tier = tier;
|
||||
this.mode = mode;
|
||||
m_Materials = materials != null ? (NBShaderMaterialBuildInfo[])materials.Clone() : new NBShaderMaterialBuildInfo[0];
|
||||
m_AllowedManagedKeywords = allowedManagedKeywords != null ? (string[])allowedManagedKeywords.Clone() : new string[0];
|
||||
m_AllowedPassFeatures = NBShaderBuildInfoUtility.ToCatalogOrderedPassFeatures(allowedPassFeatures);
|
||||
m_Variants = BuildUniqueVariants(m_Materials);
|
||||
m_IncludedPassNames = BuildIncludedPassNames(m_Materials);
|
||||
}
|
||||
|
||||
private static NBShaderVariantBuildInfo[] BuildUniqueVariants(NBShaderMaterialBuildInfo[] materials)
|
||||
{
|
||||
var result = new List<NBShaderVariantBuildInfo>();
|
||||
var keys = new HashSet<string>(StringComparer.Ordinal);
|
||||
for (var i = 0; i < materials.Length; i++)
|
||||
{
|
||||
var material = materials[i];
|
||||
if (material == null)
|
||||
continue;
|
||||
|
||||
var materialVariants = material.variants;
|
||||
for (var v = 0; v < materialVariants.Length; v++)
|
||||
{
|
||||
var variant = materialVariants[v];
|
||||
if (variant == null)
|
||||
continue;
|
||||
|
||||
var key = variant.GetStableKey();
|
||||
if (keys.Add(key))
|
||||
result.Add(variant);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
private static string[] BuildIncludedPassNames(NBShaderMaterialBuildInfo[] materials)
|
||||
{
|
||||
var result = new List<string>();
|
||||
var set = new HashSet<string>(StringComparer.Ordinal);
|
||||
for (var i = 0; i < materials.Length; i++)
|
||||
{
|
||||
var material = materials[i];
|
||||
if (material == null)
|
||||
continue;
|
||||
|
||||
var passNames = material.includedPassNames;
|
||||
for (var p = 0; p < passNames.Length; p++)
|
||||
{
|
||||
var passName = passNames[p];
|
||||
if (!string.IsNullOrEmpty(passName) && set.Add(passName))
|
||||
result.Add(passName);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class NBShaderMaterialBuildInfo
|
||||
{
|
||||
private readonly string[] m_EffectiveKeywords;
|
||||
private readonly string[] m_StrippedManagedKeywords;
|
||||
private readonly string[] m_AllowedManagedKeywords;
|
||||
private readonly string[] m_AllowedPassFeatures;
|
||||
private readonly NBShaderPassBuildInfo[] m_Passes;
|
||||
private readonly NBShaderVariantBuildInfo[] m_Variants;
|
||||
private readonly string[] m_IncludedPassNames;
|
||||
|
||||
public readonly Material material;
|
||||
public readonly Shader shader;
|
||||
public readonly NBShaderFeatureTier tier;
|
||||
public readonly NBShaderBuildInfoMode mode;
|
||||
|
||||
public string[] effectiveKeywords { get { return (string[])m_EffectiveKeywords.Clone(); } }
|
||||
public string[] strippedManagedKeywords { get { return (string[])m_StrippedManagedKeywords.Clone(); } }
|
||||
public string[] allowedManagedKeywords { get { return (string[])m_AllowedManagedKeywords.Clone(); } }
|
||||
public string[] allowedPassFeatures { get { return (string[])m_AllowedPassFeatures.Clone(); } }
|
||||
public NBShaderPassBuildInfo[] passes { get { return (NBShaderPassBuildInfo[])m_Passes.Clone(); } }
|
||||
public NBShaderVariantBuildInfo[] variants { get { return (NBShaderVariantBuildInfo[])m_Variants.Clone(); } }
|
||||
public string[] includedPassNames { get { return (string[])m_IncludedPassNames.Clone(); } }
|
||||
|
||||
public NBShaderMaterialBuildInfo(
|
||||
Material material,
|
||||
NBShaderFeatureTier tier,
|
||||
NBShaderBuildInfoMode mode,
|
||||
string[] allowedManagedKeywords,
|
||||
string[] effectiveKeywords,
|
||||
string[] strippedManagedKeywords,
|
||||
NBShaderPassBuildInfo[] passes,
|
||||
string[] allowedPassFeatures = null)
|
||||
{
|
||||
this.material = material;
|
||||
shader = material != null ? material.shader : null;
|
||||
this.tier = tier;
|
||||
this.mode = mode;
|
||||
m_AllowedManagedKeywords = NBShaderBuildInfoUtility.ToCatalogOrderedManagedKeywords(allowedManagedKeywords);
|
||||
m_AllowedPassFeatures = NBShaderBuildInfoUtility.ToCatalogOrderedPassFeatures(allowedPassFeatures);
|
||||
m_EffectiveKeywords = NBShaderBuildInfoUtility.ToCatalogOrderedManagedKeywords(effectiveKeywords);
|
||||
m_StrippedManagedKeywords = NBShaderBuildInfoUtility.ToCatalogOrderedManagedKeywords(strippedManagedKeywords);
|
||||
m_Passes = passes != null ? (NBShaderPassBuildInfo[])passes.Clone() : new NBShaderPassBuildInfo[0];
|
||||
m_IncludedPassNames = BuildIncludedPassNames(m_Passes);
|
||||
m_Variants = BuildVariants();
|
||||
}
|
||||
|
||||
private NBShaderVariantBuildInfo[] BuildVariants()
|
||||
{
|
||||
if (shader == null)
|
||||
return new NBShaderVariantBuildInfo[0];
|
||||
|
||||
var result = new List<NBShaderVariantBuildInfo>();
|
||||
var materialKeywords = NBShaderBuildInfoUtility.GetCatalogExternalMaterialKeywords(material);
|
||||
for (var i = 0; i < m_Passes.Length; i++)
|
||||
{
|
||||
var pass = m_Passes[i];
|
||||
if (pass == null || !pass.included)
|
||||
continue;
|
||||
|
||||
result.Add(new NBShaderVariantBuildInfo(
|
||||
shader,
|
||||
pass.passName,
|
||||
pass.passType,
|
||||
NBShaderBuildInfoUtility.BuildShaderVariantKeywordsForPass(
|
||||
pass.passName,
|
||||
pass.passType,
|
||||
m_EffectiveKeywords,
|
||||
materialKeywords)));
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
private static string[] BuildIncludedPassNames(NBShaderPassBuildInfo[] passes)
|
||||
{
|
||||
var result = new List<string>();
|
||||
for (var i = 0; i < passes.Length; i++)
|
||||
{
|
||||
var pass = passes[i];
|
||||
if (pass != null && pass.included && !string.IsNullOrEmpty(pass.passName))
|
||||
result.Add(pass.passName);
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class NBShaderPassBuildInfo
|
||||
{
|
||||
public readonly string passName;
|
||||
public readonly PassType passType;
|
||||
public readonly bool enabledByMaterial;
|
||||
public readonly bool allowedByTier;
|
||||
public readonly bool included;
|
||||
public readonly string reason;
|
||||
|
||||
public NBShaderPassBuildInfo(
|
||||
string passName,
|
||||
PassType passType,
|
||||
bool enabledByMaterial,
|
||||
bool allowedByTier,
|
||||
bool included,
|
||||
string reason)
|
||||
{
|
||||
this.passName = passName;
|
||||
this.passType = passType;
|
||||
this.enabledByMaterial = enabledByMaterial;
|
||||
this.allowedByTier = allowedByTier;
|
||||
this.included = included;
|
||||
this.reason = reason;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class NBShaderVariantBuildInfo
|
||||
{
|
||||
private readonly string[] m_Keywords;
|
||||
|
||||
public readonly Shader shader;
|
||||
public readonly string passName;
|
||||
public readonly PassType passType;
|
||||
public string[] keywords { get { return (string[])m_Keywords.Clone(); } }
|
||||
|
||||
public NBShaderVariantBuildInfo(Shader shader, string passName, PassType passType, string[] keywords)
|
||||
{
|
||||
this.shader = shader;
|
||||
this.passName = passName;
|
||||
this.passType = passType;
|
||||
m_Keywords = NBShaderBuildInfoUtility.ToShaderVariantOrderedKeywords(keywords);
|
||||
}
|
||||
|
||||
public string GetStableKey()
|
||||
{
|
||||
string shaderName = shader != null ? shader.name : string.Empty;
|
||||
return shaderName + "|" + passName + "|" + passType + "|" + string.Join(";", m_Keywords);
|
||||
}
|
||||
}
|
||||
|
||||
internal static class NBShaderBuildInfoUtility
|
||||
{
|
||||
public static PassType GetPassType(string passName)
|
||||
{
|
||||
if (passName == "SRPDefaultUnlit")
|
||||
return PassType.ScriptableRenderPipelineDefaultUnlit;
|
||||
if (passName == "ShadowCaster")
|
||||
return PassType.ShadowCaster;
|
||||
return PassType.ScriptableRenderPipeline;
|
||||
}
|
||||
|
||||
public static string[] FilterKeywordsForPass(string passName, PassType passType, IEnumerable<string> keywords)
|
||||
{
|
||||
var declaredKeywords = GetDeclaredKeywords(passName, passType);
|
||||
var result = new List<string>();
|
||||
var source = ToCatalogOrderedManagedKeywords(keywords);
|
||||
for (var i = 0; i < source.Length; i++)
|
||||
{
|
||||
var keyword = source[i];
|
||||
if (declaredKeywords.Contains(keyword))
|
||||
result.Add(keyword);
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
public static string[] BuildShaderVariantKeywordsForPass(
|
||||
string passName,
|
||||
PassType passType,
|
||||
IEnumerable<string> managedKeywords,
|
||||
IEnumerable<string> externalKeywords)
|
||||
{
|
||||
var declaredKeywords = GetDeclaredShaderVariantKeywords(passName, passType);
|
||||
var source = new HashSet<string>(StringComparer.Ordinal);
|
||||
|
||||
var managed = ToCatalogOrderedManagedKeywords(managedKeywords);
|
||||
for (var i = 0; i < managed.Length; i++)
|
||||
{
|
||||
var keyword = managed[i];
|
||||
source.Add(keyword);
|
||||
if (string.Equals(keyword, "_FX_LIGHT_MODE_SIX_WAY", StringComparison.Ordinal))
|
||||
source.Add("EVALUATE_SH_VERTEX");
|
||||
}
|
||||
|
||||
if (externalKeywords != null)
|
||||
{
|
||||
foreach (var keyword in externalKeywords)
|
||||
{
|
||||
if (IsCatalogExternalShaderKeyword(keyword))
|
||||
source.Add(keyword);
|
||||
}
|
||||
}
|
||||
|
||||
var result = new List<string>();
|
||||
for (var i = 0; i < NBShaderFeatureCatalog.RawKeywords.Length; i++)
|
||||
{
|
||||
var keyword = NBShaderFeatureCatalog.RawKeywords[i];
|
||||
if (source.Contains(keyword) && declaredKeywords.Contains(keyword))
|
||||
result.Add(keyword);
|
||||
}
|
||||
|
||||
for (var i = 0; i < CatalogExternalShaderKeywordOrder.Length; i++)
|
||||
{
|
||||
var keyword = CatalogExternalShaderKeywordOrder[i];
|
||||
if (source.Contains(keyword) && declaredKeywords.Contains(keyword))
|
||||
result.Add(keyword);
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
public static string[] GetCatalogExternalMaterialKeywords(Material material)
|
||||
{
|
||||
if (material == null || material.shaderKeywords == null || material.shaderKeywords.Length == 0)
|
||||
return new string[0];
|
||||
|
||||
var result = new List<string>();
|
||||
var source = new HashSet<string>(material.shaderKeywords, StringComparer.Ordinal);
|
||||
for (var i = 0; i < CatalogExternalShaderKeywordOrder.Length; i++)
|
||||
{
|
||||
var keyword = CatalogExternalShaderKeywordOrder[i];
|
||||
if (source.Contains(keyword))
|
||||
result.Add(keyword);
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
public static string[] ToCatalogOrderedManagedKeywords(IEnumerable<string> keywords)
|
||||
{
|
||||
var set = new HashSet<string>(StringComparer.Ordinal);
|
||||
if (keywords != null)
|
||||
{
|
||||
foreach (var keyword in keywords)
|
||||
{
|
||||
if (NBShaderFeatureCatalog.IsManagedKeyword(keyword))
|
||||
set.Add(keyword);
|
||||
}
|
||||
}
|
||||
|
||||
if (set.Count == 0)
|
||||
return new string[0];
|
||||
|
||||
var result = new List<string>();
|
||||
for (var i = 0; i < NBShaderFeatureCatalog.RawKeywords.Length; i++)
|
||||
{
|
||||
var keyword = NBShaderFeatureCatalog.RawKeywords[i];
|
||||
if (set.Contains(keyword))
|
||||
result.Add(keyword);
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
public static string[] ToShaderVariantOrderedKeywords(IEnumerable<string> keywords)
|
||||
{
|
||||
var set = new HashSet<string>(StringComparer.Ordinal);
|
||||
if (keywords != null)
|
||||
{
|
||||
foreach (var keyword in keywords)
|
||||
{
|
||||
if (NBShaderFeatureCatalog.IsManagedKeyword(keyword) ||
|
||||
IsCatalogExternalShaderKeyword(keyword))
|
||||
{
|
||||
set.Add(keyword);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (set.Count == 0)
|
||||
return new string[0];
|
||||
|
||||
var result = new List<string>();
|
||||
for (var i = 0; i < NBShaderFeatureCatalog.RawKeywords.Length; i++)
|
||||
{
|
||||
var keyword = NBShaderFeatureCatalog.RawKeywords[i];
|
||||
if (set.Contains(keyword))
|
||||
result.Add(keyword);
|
||||
}
|
||||
|
||||
for (var i = 0; i < CatalogExternalShaderKeywordOrder.Length; i++)
|
||||
{
|
||||
var keyword = CatalogExternalShaderKeywordOrder[i];
|
||||
if (set.Contains(keyword))
|
||||
result.Add(keyword);
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
public static string[] ToCatalogOrderedPassFeatures(IEnumerable<string> passFeatures)
|
||||
{
|
||||
var set = new HashSet<string>(StringComparer.Ordinal);
|
||||
if (passFeatures != null)
|
||||
{
|
||||
foreach (var passFeature in passFeatures)
|
||||
{
|
||||
if (NBShaderPassFeatureCatalog.IsManagedPassFeature(passFeature))
|
||||
set.Add(passFeature);
|
||||
}
|
||||
}
|
||||
|
||||
if (set.Count == 0)
|
||||
return new string[0];
|
||||
|
||||
var result = new List<string>();
|
||||
for (var i = 0; i < NBShaderPassFeatureCatalog.RawPassFeatureIds.Length; i++)
|
||||
{
|
||||
var passFeature = NBShaderPassFeatureCatalog.RawPassFeatureIds[i];
|
||||
if (set.Contains(passFeature))
|
||||
result.Add(passFeature);
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
public static bool ContainsPassName(string[] passNames, string passName)
|
||||
{
|
||||
if (passNames == null || string.IsNullOrEmpty(passName))
|
||||
return false;
|
||||
|
||||
for (var i = 0; i < passNames.Length; i++)
|
||||
{
|
||||
if (string.Equals(passNames[i], passName, StringComparison.Ordinal))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsSubsetOf(string[] subset, string[] superset)
|
||||
{
|
||||
var set = new HashSet<string>(superset ?? new string[0], StringComparer.Ordinal);
|
||||
if (subset == null)
|
||||
return true;
|
||||
|
||||
for (var i = 0; i < subset.Length; i++)
|
||||
{
|
||||
if (!set.Contains(subset[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool AreKeywordSetsEqual(string[] a, string[] b)
|
||||
{
|
||||
var normalizedA = ToCatalogOrderedManagedKeywords(a);
|
||||
var normalizedB = ToCatalogOrderedManagedKeywords(b);
|
||||
if (normalizedA.Length != normalizedB.Length)
|
||||
return false;
|
||||
|
||||
for (var i = 0; i < normalizedA.Length; i++)
|
||||
{
|
||||
if (!string.Equals(normalizedA[i], normalizedB[i], StringComparison.Ordinal))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static HashSet<string> GetDeclaredKeywords(string passName, PassType passType)
|
||||
{
|
||||
if (passType == PassType.ShadowCaster)
|
||||
return DeclaredDepthShadowKeywords;
|
||||
|
||||
if (string.Equals(passName, "DepthOnly", StringComparison.Ordinal))
|
||||
return DeclaredDepthShadowKeywords;
|
||||
|
||||
if (string.Equals(passName, "NBCameraOpaqueDistortPass", StringComparison.Ordinal) ||
|
||||
string.Equals(passName, "NBDeferredDistortPass", StringComparison.Ordinal))
|
||||
{
|
||||
return DeclaredDistortKeywords;
|
||||
}
|
||||
|
||||
if (string.Equals(passName, "Universal2D", StringComparison.Ordinal))
|
||||
return DeclaredUniversal2DKeywords;
|
||||
|
||||
return DeclaredForwardKeywords;
|
||||
}
|
||||
|
||||
private static HashSet<string> GetDeclaredShaderVariantKeywords(string passName, PassType passType)
|
||||
{
|
||||
if (passType == PassType.ShadowCaster)
|
||||
return DeclaredShadowCasterShaderVariantKeywords;
|
||||
|
||||
if (string.Equals(passName, "DepthOnly", StringComparison.Ordinal))
|
||||
return DeclaredDepthOnlyShaderVariantKeywords;
|
||||
|
||||
if (string.Equals(passName, "NBCameraOpaqueDistortPass", StringComparison.Ordinal) ||
|
||||
string.Equals(passName, "NBDeferredDistortPass", StringComparison.Ordinal))
|
||||
{
|
||||
return DeclaredDistortShaderVariantKeywords;
|
||||
}
|
||||
|
||||
if (string.Equals(passName, "Universal2D", StringComparison.Ordinal))
|
||||
return DeclaredUniversal2DShaderVariantKeywords;
|
||||
|
||||
return DeclaredForwardShaderVariantKeywords;
|
||||
}
|
||||
|
||||
private static bool IsCatalogExternalShaderKeyword(string keyword)
|
||||
{
|
||||
return !string.IsNullOrEmpty(keyword) && CatalogExternalShaderKeywordSet.Contains(keyword);
|
||||
}
|
||||
|
||||
private static readonly string[] CatalogExternalShaderKeywordOrder =
|
||||
{
|
||||
"EVALUATE_SH_VERTEX",
|
||||
"EVALUATE_SH_MIXED",
|
||||
"SOFT_UI_FRAME",
|
||||
"UNITY_UI_CLIP_RECT",
|
||||
"_ADDITIONAL_LIGHTS_VERTEX",
|
||||
"_ADDITIONAL_LIGHTS",
|
||||
"_CASTING_PUNCTUAL_LIGHT_SHADOW"
|
||||
};
|
||||
|
||||
private static readonly HashSet<string> CatalogExternalShaderKeywordSet =
|
||||
new HashSet<string>(CatalogExternalShaderKeywordOrder, StringComparer.Ordinal);
|
||||
|
||||
private static readonly HashSet<string> DeclaredForwardKeywords = BuildDeclaredKeywordSet(
|
||||
"NB_DEBUG_DISSOLVE",
|
||||
"NB_DEBUG_DISTORT",
|
||||
"NB_DEBUG_FRESNEL",
|
||||
"NB_DEBUG_MASK",
|
||||
"NB_DEBUG_PNOISE",
|
||||
"NB_DEBUG_VERTEX_OFFSET",
|
||||
"VFX_SIX_WAY_ABSORPTION",
|
||||
"_ALPHAMODULATE_ON",
|
||||
"_ALPHAPREMULTIPLY_ON",
|
||||
"_ALPHATEST_ON",
|
||||
"_CHROMATIC_ABERRATION",
|
||||
"_COLORMAPBLEND",
|
||||
"_COLOR_RAMP",
|
||||
"_COLOR_RAMP_MAP",
|
||||
"_DEPTH_DECAL",
|
||||
"_DEPTH_OUTLINE",
|
||||
"_DISSOLVE",
|
||||
"_DISSOLVE_MASK",
|
||||
"_DISSOLVE_RAMP",
|
||||
"_DISSOLVE_RAMP_MAP",
|
||||
"_DISTANCE_FADE",
|
||||
"_DISTORT_REFRACTION",
|
||||
"_EMISSION",
|
||||
"_FLIPBOOKBLENDING_ON",
|
||||
"_FRESNEL",
|
||||
"_FX_LIGHT_MODE_BLINN_PHONG",
|
||||
"_FX_LIGHT_MODE_HALF_LAMBERT",
|
||||
"_FX_LIGHT_MODE_PBR",
|
||||
"_FX_LIGHT_MODE_SIX_WAY",
|
||||
"_FX_LIGHT_MODE_UNLIT",
|
||||
"_HOUDINI_VAT_DYNAMIC_REMESH",
|
||||
"_HOUDINI_VAT_PARTICLE_SPRITE",
|
||||
"_HOUDINI_VAT_RIGIDBODY",
|
||||
"_HOUDINI_VAT_SOFTBODY",
|
||||
"_MASKMAP_ON",
|
||||
"_MASKMAP2_ON",
|
||||
"_MASKMAP3_ON",
|
||||
"_MATCAP",
|
||||
"_NOISEMAP",
|
||||
"_NOISE_MASKMAP",
|
||||
"_NORMALMAP",
|
||||
"_OVERRIDE_Z",
|
||||
"_PARALLAX_MAPPING",
|
||||
"_PROGRAM_NOISE",
|
||||
"_PROGRAM_NOISE_SIMPLE",
|
||||
"_PROGRAM_NOISE_VORONOI",
|
||||
"_SCRIPTABLETIME",
|
||||
"_SHARED_UV",
|
||||
"_SOFTPARTICLES_ON",
|
||||
"_SPECULAR_COLOR",
|
||||
"_STENCIL_WITHOUT_PLAYER",
|
||||
"_TYFLOW_VAT_ABSOLUTE",
|
||||
"_TYFLOW_VAT_RELATIVE",
|
||||
"_TYFLOW_VAT_SKIN_PR",
|
||||
"_TYFLOW_VAT_SKIN_PRSAVE",
|
||||
"_TYFLOW_VAT_SKIN_PRSXYZ",
|
||||
"_TYFLOW_VAT_SKIN_R",
|
||||
"_UNSCALETIME",
|
||||
"_VAT",
|
||||
"_VAT_HOUDINI",
|
||||
"_VAT_TYFLOW",
|
||||
"_VERTEX_OFFSET",
|
||||
"_VERTEX_OFFSET_MASKMAP");
|
||||
|
||||
private static readonly HashSet<string> DeclaredDistortKeywords = BuildDeclaredKeywordSet(
|
||||
"_ALPHAMODULATE_ON",
|
||||
"_ALPHAPREMULTIPLY_ON",
|
||||
"_ALPHATEST_ON",
|
||||
"_CHROMATIC_ABERRATION",
|
||||
"_DEPTH_DECAL",
|
||||
"_DEPTH_OUTLINE",
|
||||
"_DISSOLVE",
|
||||
"_DISSOLVE_MASK",
|
||||
"_DISSOLVE_RAMP",
|
||||
"_DISSOLVE_RAMP_MAP",
|
||||
"_DISTANCE_FADE",
|
||||
"_DISTORT_REFRACTION",
|
||||
"_FLIPBOOKBLENDING_ON",
|
||||
"_FRESNEL",
|
||||
"_FX_LIGHT_MODE_UNLIT",
|
||||
"_MASKMAP_ON",
|
||||
"_MASKMAP2_ON",
|
||||
"_MASKMAP3_ON",
|
||||
"_NOISEMAP",
|
||||
"_NOISE_MASKMAP",
|
||||
"_NORMALMAP",
|
||||
"_PROGRAM_NOISE",
|
||||
"_PROGRAM_NOISE_SIMPLE",
|
||||
"_PROGRAM_NOISE_VORONOI",
|
||||
"_SCRIPTABLETIME",
|
||||
"_SHARED_UV",
|
||||
"_SOFTPARTICLES_ON",
|
||||
"_STENCIL_WITHOUT_PLAYER",
|
||||
"_UNSCALETIME",
|
||||
"_VERTEX_OFFSET",
|
||||
"_VERTEX_OFFSET_MASKMAP");
|
||||
|
||||
private static readonly HashSet<string> DeclaredDepthShadowKeywords = BuildDeclaredKeywordSet(
|
||||
"_ALPHATEST_ON",
|
||||
"_DISSOLVE",
|
||||
"_DISSOLVE_MASK",
|
||||
"_FLIPBOOKBLENDING_ON",
|
||||
"_HOUDINI_VAT_DYNAMIC_REMESH",
|
||||
"_HOUDINI_VAT_PARTICLE_SPRITE",
|
||||
"_HOUDINI_VAT_RIGIDBODY",
|
||||
"_HOUDINI_VAT_SOFTBODY",
|
||||
"_MASKMAP_ON",
|
||||
"_MASKMAP2_ON",
|
||||
"_MASKMAP3_ON",
|
||||
"_NOISEMAP",
|
||||
"_NOISE_MASKMAP",
|
||||
"_PROGRAM_NOISE",
|
||||
"_PROGRAM_NOISE_SIMPLE",
|
||||
"_PROGRAM_NOISE_VORONOI",
|
||||
"_SCRIPTABLETIME",
|
||||
"_SHARED_UV",
|
||||
"_TYFLOW_VAT_ABSOLUTE",
|
||||
"_TYFLOW_VAT_RELATIVE",
|
||||
"_TYFLOW_VAT_SKIN_PR",
|
||||
"_TYFLOW_VAT_SKIN_PRSAVE",
|
||||
"_TYFLOW_VAT_SKIN_PRSXYZ",
|
||||
"_TYFLOW_VAT_SKIN_R",
|
||||
"_UNSCALETIME",
|
||||
"_VAT",
|
||||
"_VAT_HOUDINI",
|
||||
"_VAT_TYFLOW",
|
||||
"_VERTEX_OFFSET",
|
||||
"_VERTEX_OFFSET_MASKMAP");
|
||||
|
||||
private static readonly HashSet<string> DeclaredUniversal2DKeywords = BuildDeclaredKeywordSet(
|
||||
"_ALPHAMODULATE_ON",
|
||||
"_ALPHAPREMULTIPLY_ON",
|
||||
"_ALPHATEST_ON",
|
||||
"_CHROMATIC_ABERRATION",
|
||||
"_COLORMAPBLEND",
|
||||
"_COLOR_RAMP",
|
||||
"_COLOR_RAMP_MAP",
|
||||
"_DISSOLVE",
|
||||
"_DISSOLVE_MASK",
|
||||
"_DISSOLVE_RAMP",
|
||||
"_DISSOLVE_RAMP_MAP",
|
||||
"_EMISSION",
|
||||
"_FLIPBOOKBLENDING_ON",
|
||||
"_FRESNEL",
|
||||
"_FX_LIGHT_MODE_UNLIT",
|
||||
"_MASKMAP_ON",
|
||||
"_MASKMAP2_ON",
|
||||
"_MASKMAP3_ON",
|
||||
"_NOISEMAP",
|
||||
"_NOISE_MASKMAP",
|
||||
"_NORMALMAP",
|
||||
"_PARCUSTOMDATA_ON",
|
||||
"_PROGRAM_NOISE",
|
||||
"_PROGRAM_NOISE_SIMPLE",
|
||||
"_PROGRAM_NOISE_VORONOI",
|
||||
"_SCREEN_DISTORT_MODE",
|
||||
"_SCRIPTABLETIME",
|
||||
"_SHARED_UV",
|
||||
"_STENCIL_WITHOUT_PLAYER",
|
||||
"_UNSCALETIME",
|
||||
"_VERTEX_OFFSET",
|
||||
"_VERTEX_OFFSET_MASKMAP");
|
||||
|
||||
private static readonly HashSet<string> DeclaredForwardShaderVariantKeywords =
|
||||
BuildDeclaredShaderVariantKeywordSet(
|
||||
DeclaredForwardKeywords,
|
||||
"SOFT_UI_FRAME",
|
||||
"EVALUATE_SH_MIXED",
|
||||
"EVALUATE_SH_VERTEX",
|
||||
"UNITY_UI_CLIP_RECT",
|
||||
"_ADDITIONAL_LIGHTS_VERTEX",
|
||||
"_ADDITIONAL_LIGHTS");
|
||||
|
||||
private static readonly HashSet<string> DeclaredDistortShaderVariantKeywords =
|
||||
BuildDeclaredShaderVariantKeywordSet(
|
||||
DeclaredDistortKeywords,
|
||||
"SOFT_UI_FRAME",
|
||||
"EVALUATE_SH_MIXED",
|
||||
"EVALUATE_SH_VERTEX");
|
||||
|
||||
private static readonly HashSet<string> DeclaredDepthOnlyShaderVariantKeywords =
|
||||
BuildDeclaredShaderVariantKeywordSet(
|
||||
DeclaredDepthShadowKeywords);
|
||||
|
||||
private static readonly HashSet<string> DeclaredShadowCasterShaderVariantKeywords =
|
||||
BuildDeclaredShaderVariantKeywordSet(
|
||||
DeclaredDepthShadowKeywords,
|
||||
"_CASTING_PUNCTUAL_LIGHT_SHADOW");
|
||||
|
||||
private static readonly HashSet<string> DeclaredUniversal2DShaderVariantKeywords =
|
||||
BuildDeclaredShaderVariantKeywordSet(
|
||||
DeclaredUniversal2DKeywords,
|
||||
"SOFT_UI_FRAME",
|
||||
"EVALUATE_SH_MIXED",
|
||||
"EVALUATE_SH_VERTEX",
|
||||
"UNITY_UI_CLIP_RECT",
|
||||
"_ADDITIONAL_LIGHTS_VERTEX",
|
||||
"_ADDITIONAL_LIGHTS");
|
||||
|
||||
private static HashSet<string> BuildDeclaredKeywordSet(params string[] keywords)
|
||||
{
|
||||
var result = new HashSet<string>(StringComparer.Ordinal);
|
||||
if (keywords == null)
|
||||
return result;
|
||||
|
||||
for (var i = 0; i < keywords.Length; i++)
|
||||
{
|
||||
var keyword = keywords[i];
|
||||
if (NBShaderFeatureCatalog.IsManagedKeyword(keyword))
|
||||
result.Add(keyword);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static HashSet<string> BuildDeclaredShaderVariantKeywordSet(
|
||||
HashSet<string> managedKeywords,
|
||||
params string[] externalKeywords)
|
||||
{
|
||||
var result = new HashSet<string>(managedKeywords ?? new HashSet<string>(), StringComparer.Ordinal);
|
||||
if (externalKeywords == null)
|
||||
return result;
|
||||
|
||||
for (var i = 0; i < externalKeywords.Length; i++)
|
||||
{
|
||||
var keyword = externalKeywords[i];
|
||||
if (IsCatalogExternalShaderKeyword(keyword))
|
||||
result.Add(keyword);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 48cf3e4ab2374d47bbb51283b80ad2d2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NBShader;
|
||||
|
||||
namespace NBShaders2.Editor.FeatureLevel
|
||||
{
|
||||
public static class NBShaderFeatureLevelBuildStripOverride
|
||||
{
|
||||
private static readonly Stack<NBShaderFeatureTier> s_OverrideStack = new Stack<NBShaderFeatureTier>();
|
||||
|
||||
public static bool hasOverride { get { return s_OverrideStack.Count > 0; } }
|
||||
|
||||
internal static bool TryGetCurrentTier(out NBShaderFeatureTier tier)
|
||||
{
|
||||
if (s_OverrideStack.Count > 0)
|
||||
{
|
||||
tier = s_OverrideStack.Peek();
|
||||
return true;
|
||||
}
|
||||
|
||||
tier = NBShaderFeatureTier.Ultra;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static IDisposable PushExplicitTier(NBShaderFeatureTier tier)
|
||||
{
|
||||
NBShaderVariantStripper.ResetMissingExplicitTierWarning();
|
||||
s_OverrideStack.Push(tier);
|
||||
return new Scope();
|
||||
}
|
||||
|
||||
private sealed class Scope : IDisposable
|
||||
{
|
||||
private bool m_Disposed;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (m_Disposed)
|
||||
return;
|
||||
m_Disposed = true;
|
||||
if (s_OverrideStack.Count > 0)
|
||||
s_OverrideStack.Pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 21bd03c909bfb4e9f97d2c9524827cce
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NBShader;
|
||||
|
||||
namespace NBShaders2.Editor.FeatureLevel
|
||||
{
|
||||
public static class NBShaderFeatureLevelCatalog
|
||||
{
|
||||
public const string ShaderName = NBShaderFeatureCatalog.ShaderName;
|
||||
|
||||
public static string[] ManagedKeywords
|
||||
{
|
||||
get { return (string[])NBShaderFeatureCatalog.RawKeywords.Clone(); }
|
||||
}
|
||||
|
||||
public static string[] ManagedPassFeatures
|
||||
{
|
||||
get { return (string[])NBShaderPassFeatureCatalog.RawPassFeatureIds.Clone(); }
|
||||
}
|
||||
|
||||
public static bool IsManagedKeyword(string keyword)
|
||||
{
|
||||
return NBShaderFeatureCatalog.IsManagedKeyword(keyword);
|
||||
}
|
||||
|
||||
public static bool IsManagedPassFeature(string passFeatureId)
|
||||
{
|
||||
return NBShaderPassFeatureCatalog.IsManagedPassFeature(passFeatureId);
|
||||
}
|
||||
|
||||
public static bool TryGetManagedPassFeatureInfo(
|
||||
string passFeatureId,
|
||||
out string passName,
|
||||
out string displayName)
|
||||
{
|
||||
NBShaderPassFeatureInfo feature;
|
||||
if (NBShaderPassFeatureCatalog.TryGetPassFeature(passFeatureId, out feature))
|
||||
{
|
||||
passName = feature.passName;
|
||||
displayName = feature.displayName;
|
||||
return true;
|
||||
}
|
||||
|
||||
passName = string.Empty;
|
||||
displayName = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryGetManagedPassFeatureByPassName(
|
||||
string passName,
|
||||
out string passFeatureId)
|
||||
{
|
||||
NBShaderPassFeatureInfo feature;
|
||||
if (NBShaderPassFeatureCatalog.TryGetPassFeatureByPassName(passName, out feature))
|
||||
{
|
||||
passFeatureId = feature.id;
|
||||
return true;
|
||||
}
|
||||
|
||||
passFeatureId = string.Empty;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8da3dc0041f2c47a38e0d1c1c20dc493
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,285 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NBShader;
|
||||
using UnityEditor.Rendering;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace NBShaders2.Editor.FeatureLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Public Editor API for CI/custom build scripts. Override scopes are process-local and never saved
|
||||
/// into ProjectSettings/NBShaderFeatureLevels.asset.
|
||||
/// </summary>
|
||||
public static class NBShaderFeatureLevelEditorAPI
|
||||
{
|
||||
public static IDisposable OverrideBuildStripExplicitTier(NBShaderFeatureTier tier)
|
||||
{
|
||||
return NBShaderFeatureLevelBuildStripOverride.PushExplicitTier(tier);
|
||||
}
|
||||
|
||||
public static string DefaultGeneratedRuntimeSettingsAssetPath
|
||||
{
|
||||
get { return NBShaderRuntimeSettingsSynchronizer.DefaultGeneratedRuntimeSettingsAssetPath; }
|
||||
}
|
||||
|
||||
public static HashSet<string> GetAllowedManagedKeywords(NBShaderFeatureTier tier)
|
||||
{
|
||||
return NBShaderFeatureLevelProjectSettings.instance.GetAllowedKeywordSetForBuildInfoNoSave(tier);
|
||||
}
|
||||
|
||||
public static HashSet<string> GetAllowedManagedPassFeatures(NBShaderFeatureTier tier)
|
||||
{
|
||||
return NBShaderFeatureLevelProjectSettings.instance.GetAllowedPassFeatureSetForBuildInfoNoSave(tier);
|
||||
}
|
||||
|
||||
public static bool TryGetMaterialBuildInfo(
|
||||
Material material,
|
||||
NBShaderFeatureTier tier,
|
||||
out NBShaderMaterialBuildInfo buildInfo,
|
||||
NBShaderBuildInfoMode mode = NBShaderBuildInfoMode.ExactMaterialVariants)
|
||||
{
|
||||
buildInfo = null;
|
||||
if (!NBShaderMaterialIntentResolver.IsNBShaderMaterial(material))
|
||||
return false;
|
||||
|
||||
buildInfo = GetBuildInfo(material, tier, mode);
|
||||
return buildInfo != null;
|
||||
}
|
||||
|
||||
public static NBShaderMaterialBuildInfo GetBuildInfo(
|
||||
Material material,
|
||||
NBShaderFeatureTier tier,
|
||||
NBShaderBuildInfoMode mode = NBShaderBuildInfoMode.ExactMaterialVariants)
|
||||
{
|
||||
if (!NBShaderMaterialIntentResolver.IsNBShaderMaterial(material))
|
||||
return null;
|
||||
|
||||
var allowedKeywords = NBShaderFeatureLevelProjectSettings.instance.GetAllowedKeywordSetForBuildInfoNoSave(tier);
|
||||
var allowedPassFeatures = NBShaderFeatureLevelProjectSettings.instance.GetAllowedPassFeatureSetForBuildInfoNoSave(tier);
|
||||
var intent = NBShaderMaterialIntentResolver.Resolve(material, tier, allowedKeywords, allowedPassFeatures);
|
||||
return new NBShaderMaterialBuildInfo(
|
||||
material,
|
||||
tier,
|
||||
mode,
|
||||
NBShaderBuildInfoUtility.ToCatalogOrderedManagedKeywords(allowedKeywords),
|
||||
intent.effectiveKeywords,
|
||||
intent.strippedManagedKeywords,
|
||||
BuildPassInfo(intent.passes),
|
||||
NBShaderBuildInfoUtility.ToCatalogOrderedPassFeatures(allowedPassFeatures));
|
||||
}
|
||||
|
||||
public static NBShaderBuildInfoSet GetBuildInfo(
|
||||
NBShaderFeatureTier tier,
|
||||
IEnumerable<Material> materials,
|
||||
NBShaderBuildInfoMode mode = NBShaderBuildInfoMode.ExactMaterialVariants)
|
||||
{
|
||||
var result = new List<NBShaderMaterialBuildInfo>();
|
||||
if (materials != null)
|
||||
{
|
||||
foreach (var material in materials)
|
||||
{
|
||||
NBShaderMaterialBuildInfo buildInfo;
|
||||
if (TryGetMaterialBuildInfo(material, tier, out buildInfo, mode))
|
||||
result.Add(buildInfo);
|
||||
}
|
||||
}
|
||||
|
||||
if (result.Count == 0)
|
||||
return null;
|
||||
|
||||
return new NBShaderBuildInfoSet(
|
||||
tier,
|
||||
mode,
|
||||
result.ToArray(),
|
||||
NBShaderBuildInfoUtility.ToCatalogOrderedManagedKeywords(
|
||||
NBShaderFeatureLevelProjectSettings.instance.GetAllowedKeywordSetForBuildInfoNoSave(tier)),
|
||||
NBShaderBuildInfoUtility.ToCatalogOrderedPassFeatures(
|
||||
NBShaderFeatureLevelProjectSettings.instance.GetAllowedPassFeatureSetForBuildInfoNoSave(tier)));
|
||||
}
|
||||
|
||||
public static bool ShouldKeepVariant(
|
||||
Shader shader,
|
||||
ShaderSnippetData snippet,
|
||||
ShaderCompilerData compilerData,
|
||||
NBShaderMaterialBuildInfo buildInfo)
|
||||
{
|
||||
if (buildInfo == null)
|
||||
return true;
|
||||
|
||||
return ShouldKeepVariant(
|
||||
shader,
|
||||
snippet.passName,
|
||||
snippet.passType,
|
||||
ExtractManagedKeywords(shader, compilerData),
|
||||
buildInfo);
|
||||
}
|
||||
|
||||
public static bool ShouldKeepVariant(
|
||||
Shader shader,
|
||||
ShaderSnippetData snippet,
|
||||
ShaderCompilerData compilerData,
|
||||
NBShaderBuildInfoSet buildInfo)
|
||||
{
|
||||
if (buildInfo == null)
|
||||
return true;
|
||||
|
||||
return ShouldKeepVariant(
|
||||
shader,
|
||||
snippet.passName,
|
||||
snippet.passType,
|
||||
ExtractManagedKeywords(shader, compilerData),
|
||||
buildInfo);
|
||||
}
|
||||
|
||||
public static bool ShouldKeepVariant(
|
||||
Shader shader,
|
||||
string passName,
|
||||
PassType passType,
|
||||
IEnumerable<string> keywords,
|
||||
NBShaderMaterialBuildInfo buildInfo)
|
||||
{
|
||||
if (buildInfo == null)
|
||||
return true;
|
||||
|
||||
var set = new NBShaderBuildInfoSet(
|
||||
buildInfo.tier,
|
||||
buildInfo.mode,
|
||||
new[] { buildInfo },
|
||||
buildInfo.allowedManagedKeywords,
|
||||
buildInfo.allowedPassFeatures);
|
||||
return ShouldKeepVariant(shader, passName, passType, keywords, set);
|
||||
}
|
||||
|
||||
public static bool ShouldKeepVariant(
|
||||
Shader shader,
|
||||
string passName,
|
||||
PassType passType,
|
||||
IEnumerable<string> keywords,
|
||||
NBShaderBuildInfoSet buildInfo)
|
||||
{
|
||||
if (shader == null || shader.name != NBShaderFeatureLevelCatalog.ShaderName || buildInfo == null)
|
||||
return true;
|
||||
|
||||
var managedKeywords = NBShaderBuildInfoUtility.ToCatalogOrderedManagedKeywords(keywords);
|
||||
if (!buildInfo.hasMaterials)
|
||||
return true;
|
||||
|
||||
if (buildInfo.mode == NBShaderBuildInfoMode.TierAndPass)
|
||||
return IsPassIncluded(passName, passType, buildInfo) &&
|
||||
NBShaderBuildInfoUtility.IsSubsetOf(managedKeywords, buildInfo.allowedManagedKeywords);
|
||||
|
||||
var variants = buildInfo.variants;
|
||||
var declaredCompilerKeywords = NBShaderBuildInfoUtility.FilterKeywordsForPass(passName, passType, managedKeywords);
|
||||
for (var i = 0; i < variants.Length; i++)
|
||||
{
|
||||
var variant = variants[i];
|
||||
if (variant == null || !IsSameShader(shader, variant.shader))
|
||||
continue;
|
||||
|
||||
if (!IsPassMatch(passName, passType, variant.passName, variant.passType))
|
||||
continue;
|
||||
|
||||
if (NBShaderBuildInfoUtility.AreKeywordSetsEqual(declaredCompilerKeywords, variant.keywords))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool WriteRuntimeSettingsAsset(NBShaderFeatureRuntimeSettings asset)
|
||||
{
|
||||
return NBShaderRuntimeSettingsSynchronizer.WriteProjectSettingsToRuntimeAsset(asset);
|
||||
}
|
||||
|
||||
public static bool WriteRuntimeSettingsAssetNoSave(NBShaderFeatureRuntimeSettings asset)
|
||||
{
|
||||
return NBShaderRuntimeSettingsSynchronizer.WriteProjectSettingsSnapshotToRuntimeAssetNoSave(asset);
|
||||
}
|
||||
|
||||
public static NBShaderFeatureRuntimeSettings WriteDefaultRuntimeSettingsAsset(out string assetPath)
|
||||
{
|
||||
return NBShaderRuntimeSettingsSynchronizer.WriteDefaultGeneratedRuntimeSettingsAsset(out assetPath);
|
||||
}
|
||||
|
||||
public static NBShaderFeatureRuntimeSettings WriteRuntimeSettingsAssetOrDefault(
|
||||
NBShaderFeatureRuntimeSettings explicitAsset,
|
||||
out string assetPath)
|
||||
{
|
||||
return NBShaderRuntimeSettingsSynchronizer.WriteRuntimeSettingsAssetOrDefault(explicitAsset, out assetPath);
|
||||
}
|
||||
|
||||
private static NBShaderPassBuildInfo[] BuildPassInfo(NBShaderPassIntent[] passes)
|
||||
{
|
||||
if (passes == null || passes.Length == 0)
|
||||
return new NBShaderPassBuildInfo[0];
|
||||
|
||||
var result = new NBShaderPassBuildInfo[passes.Length];
|
||||
for (var i = 0; i < passes.Length; i++)
|
||||
{
|
||||
var pass = passes[i];
|
||||
result[i] = new NBShaderPassBuildInfo(
|
||||
pass.passName,
|
||||
NBShaderBuildInfoUtility.GetPassType(pass.passName),
|
||||
pass.enabledByMaterial,
|
||||
pass.allowedByTier,
|
||||
pass.included,
|
||||
pass.reason);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static string[] ExtractManagedKeywords(Shader shader, ShaderCompilerData compilerData)
|
||||
{
|
||||
if (shader == null)
|
||||
return new string[0];
|
||||
|
||||
var result = new List<string>();
|
||||
var keywords = compilerData.shaderKeywordSet.GetShaderKeywords();
|
||||
for (var i = 0; i < keywords.Length; i++)
|
||||
{
|
||||
var keywordName = keywords[i].name;
|
||||
if (NBShaderFeatureLevelCatalog.IsManagedKeyword(keywordName))
|
||||
result.Add(keywordName);
|
||||
}
|
||||
|
||||
return NBShaderBuildInfoUtility.ToCatalogOrderedManagedKeywords(result);
|
||||
}
|
||||
|
||||
private static bool IsPassIncluded(string passName, PassType passType, NBShaderBuildInfoSet buildInfo)
|
||||
{
|
||||
if (buildInfo == null)
|
||||
return true;
|
||||
|
||||
if (!string.IsNullOrEmpty(passName))
|
||||
return NBShaderBuildInfoUtility.ContainsPassName(buildInfo.includedPassNames, passName);
|
||||
|
||||
var variants = buildInfo.variants;
|
||||
for (var i = 0; i < variants.Length; i++)
|
||||
{
|
||||
var variant = variants[i];
|
||||
if (variant != null && variant.passType == passType)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsPassMatch(string passName, PassType passType, string targetPassName, PassType targetPassType)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(passName) && !string.IsNullOrEmpty(targetPassName))
|
||||
return string.Equals(passName, targetPassName, StringComparison.Ordinal);
|
||||
|
||||
return passType == targetPassType;
|
||||
}
|
||||
|
||||
private static bool IsSameShader(Shader a, Shader b)
|
||||
{
|
||||
if (a == null || b == null)
|
||||
return false;
|
||||
|
||||
return ReferenceEquals(a, b) || string.Equals(a.name, b.name, StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 00d0583b58944e79978bb3deeaf7bdf7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,88 @@
|
||||
using System.Collections.Generic;
|
||||
using NBShader;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBShaders2.Editor.FeatureLevel
|
||||
{
|
||||
internal static class NBShaderFeatureLevelMaterialApplier
|
||||
{
|
||||
private const string FeatureTierPropertyName = "_NBShaderFeatureTier";
|
||||
|
||||
public static bool Apply(Material material, NBShaderFeatureTier tier, bool writeTierProperty, bool applyResolvedState)
|
||||
{
|
||||
bool changed;
|
||||
return Apply(material, tier, writeTierProperty, applyResolvedState, out changed);
|
||||
}
|
||||
|
||||
public static bool Apply(
|
||||
Material material,
|
||||
NBShaderFeatureTier tier,
|
||||
bool writeTierProperty,
|
||||
bool applyResolvedState,
|
||||
out bool changed)
|
||||
{
|
||||
changed = false;
|
||||
if (!NBShaderMaterialIntentResolver.IsNBShaderMaterial(material))
|
||||
return false;
|
||||
|
||||
if (writeTierProperty &&
|
||||
material.HasProperty(FeatureTierPropertyName) &&
|
||||
!Mathf.Approximately(material.GetFloat(FeatureTierPropertyName), (float)tier))
|
||||
{
|
||||
material.SetFloat(FeatureTierPropertyName, (float)tier);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!applyResolvedState)
|
||||
return true;
|
||||
|
||||
var allowedKeywords = NBShaderFeatureLevelProjectSettings.instance.GetAllowedKeywordSetForBuildInfoNoSave(tier);
|
||||
var allowedPassFeatures = NBShaderFeatureLevelProjectSettings.instance.GetAllowedPassFeatureSetForBuildInfoNoSave(tier);
|
||||
var result = NBShaderMaterialIntentResolver.Resolve(material, tier, allowedKeywords, allowedPassFeatures);
|
||||
changed |= ApplyResolvedIntent(material, result);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool ApplyResolvedIntent(Material material, NBShaderMaterialIntentResult result)
|
||||
{
|
||||
if (material == null || result == null)
|
||||
return false;
|
||||
|
||||
var changed = false;
|
||||
var effectiveKeywords = new HashSet<string>(result.effectiveKeywords);
|
||||
for (int i = 0; i < NBShaderFeatureCatalog.RawKeywords.Length; i++)
|
||||
{
|
||||
string keyword = NBShaderFeatureCatalog.RawKeywords[i];
|
||||
changed |= SetKeyword(material, keyword, effectiveKeywords.Contains(keyword));
|
||||
}
|
||||
|
||||
changed |= SetKeyword(material, "EVALUATE_SH_VERTEX", effectiveKeywords.Contains("_FX_LIGHT_MODE_SIX_WAY"));
|
||||
|
||||
for (int i = 0; i < result.passes.Length; i++)
|
||||
{
|
||||
NBShaderPassIntent pass = result.passes[i];
|
||||
if (!string.IsNullOrEmpty(pass.passName) &&
|
||||
material.GetShaderPassEnabled(pass.passName) != pass.included)
|
||||
{
|
||||
material.SetShaderPassEnabled(pass.passName, pass.included);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
private static bool SetKeyword(Material material, string keyword, bool enabled)
|
||||
{
|
||||
if (material == null || string.IsNullOrEmpty(keyword) || material.IsKeywordEnabled(keyword) == enabled)
|
||||
return false;
|
||||
|
||||
if (enabled)
|
||||
material.EnableKeyword(keyword);
|
||||
else
|
||||
material.DisableKeyword(keyword);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8f23b4e1c9a5465cbf64079ab0d81880
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,301 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NBShader;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBShaders2.Editor.FeatureLevel
|
||||
{
|
||||
[CreateAssetMenu(fileName = "NBShaderDefaultFeatureLevels", menuName = "NBShader/Feature Level Preset")]
|
||||
public sealed class NBShaderFeatureLevelPreset : ScriptableObject
|
||||
{
|
||||
[SerializeField] private NBShaderFeatureTierKeywordSet[] m_TierKeywordSets = new NBShaderFeatureTierKeywordSet[0];
|
||||
[SerializeField] private NBShaderFeatureTierPassSet[] m_TierPassSets = new NBShaderFeatureTierPassSet[0];
|
||||
|
||||
public NBShaderFeatureTierKeywordSet[] CreateTierKeywordSets()
|
||||
{
|
||||
var result = new NBShaderFeatureTierKeywordSet[4];
|
||||
for (var i = 0; i < result.Length; i++)
|
||||
{
|
||||
var tier = (NBShaderFeatureTier)i;
|
||||
result[i] = new NBShaderFeatureTierKeywordSet
|
||||
{
|
||||
tier = tier,
|
||||
allowedKeywords = GetAllowedKeywords(tier)
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public NBShaderFeatureTierPassSet[] CreateTierPassSets()
|
||||
{
|
||||
var result = new NBShaderFeatureTierPassSet[4];
|
||||
for (var i = 0; i < result.Length; i++)
|
||||
{
|
||||
var tier = (NBShaderFeatureTier)i;
|
||||
result[i] = new NBShaderFeatureTierPassSet
|
||||
{
|
||||
tier = tier,
|
||||
allowedPassFeatures = GetAllowedPassFeatures(tier)
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public string[] GetAllowedKeywords(NBShaderFeatureTier tier)
|
||||
{
|
||||
if (m_TierKeywordSets == null)
|
||||
return new string[0];
|
||||
|
||||
for (var i = 0; i < m_TierKeywordSets.Length; i++)
|
||||
{
|
||||
var set = m_TierKeywordSets[i];
|
||||
if (set != null && set.tier == tier)
|
||||
return SanitizeKeywords(set.allowedKeywords);
|
||||
}
|
||||
|
||||
return new string[0];
|
||||
}
|
||||
|
||||
public string[] GetAllowedPassFeatures(NBShaderFeatureTier tier)
|
||||
{
|
||||
if (m_TierPassSets == null || m_TierPassSets.Length == 0)
|
||||
return NBShaderPassFeatureCatalog.GetDefaultAllowedPassFeatures(tier);
|
||||
|
||||
for (var i = 0; i < m_TierPassSets.Length; i++)
|
||||
{
|
||||
var set = m_TierPassSets[i];
|
||||
if (set != null && set.tier == tier)
|
||||
return SanitizePassFeatures(set.allowedPassFeatures);
|
||||
}
|
||||
|
||||
return NBShaderPassFeatureCatalog.GetDefaultAllowedPassFeatures(tier);
|
||||
}
|
||||
|
||||
public void SetTierKeywordSets(NBShaderFeatureTierKeywordSet[] tierKeywordSets)
|
||||
{
|
||||
m_TierKeywordSets = new NBShaderFeatureTierKeywordSet[4];
|
||||
for (var i = 0; i < m_TierKeywordSets.Length; i++)
|
||||
{
|
||||
var tier = (NBShaderFeatureTier)i;
|
||||
m_TierKeywordSets[i] = new NBShaderFeatureTierKeywordSet
|
||||
{
|
||||
tier = tier,
|
||||
allowedKeywords = GetAllowedKeywords(tierKeywordSets, tier)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTierPassSets(NBShaderFeatureTierPassSet[] tierPassSets)
|
||||
{
|
||||
m_TierPassSets = new NBShaderFeatureTierPassSet[4];
|
||||
for (var i = 0; i < m_TierPassSets.Length; i++)
|
||||
{
|
||||
var tier = (NBShaderFeatureTier)i;
|
||||
m_TierPassSets[i] = new NBShaderFeatureTierPassSet
|
||||
{
|
||||
tier = tier,
|
||||
allowedPassFeatures = GetAllowedPassFeatures(tierPassSets, tier)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static string[] SanitizeKeywords(string[] keywords)
|
||||
{
|
||||
if (keywords == null || keywords.Length == 0)
|
||||
return new string[0];
|
||||
|
||||
var allowed = new HashSet<string>(StringComparer.Ordinal);
|
||||
for (var i = 0; i < keywords.Length; i++)
|
||||
{
|
||||
var keyword = keywords[i];
|
||||
if (NBShaderFeatureLevelCatalog.IsManagedKeyword(keyword))
|
||||
allowed.Add(keyword);
|
||||
}
|
||||
|
||||
var result = new List<string>();
|
||||
var catalog = NBShaderFeatureLevelCatalog.ManagedKeywords;
|
||||
for (var i = 0; i < catalog.Length; i++)
|
||||
{
|
||||
if (allowed.Contains(catalog[i]))
|
||||
result.Add(catalog[i]);
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
private static string[] SanitizePassFeatures(string[] passFeatures)
|
||||
{
|
||||
if (passFeatures == null || passFeatures.Length == 0)
|
||||
return new string[0];
|
||||
|
||||
var allowed = new HashSet<string>(StringComparer.Ordinal);
|
||||
for (var i = 0; i < passFeatures.Length; i++)
|
||||
{
|
||||
var passFeature = passFeatures[i];
|
||||
if (NBShaderFeatureLevelCatalog.IsManagedPassFeature(passFeature))
|
||||
allowed.Add(passFeature);
|
||||
}
|
||||
|
||||
var result = new List<string>();
|
||||
var catalog = NBShaderFeatureLevelCatalog.ManagedPassFeatures;
|
||||
for (var i = 0; i < catalog.Length; i++)
|
||||
{
|
||||
if (allowed.Contains(catalog[i]))
|
||||
result.Add(catalog[i]);
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
private static string[] GetAllowedKeywords(NBShaderFeatureTierKeywordSet[] sets, NBShaderFeatureTier tier)
|
||||
{
|
||||
if (sets == null)
|
||||
return new string[0];
|
||||
|
||||
for (var i = 0; i < sets.Length; i++)
|
||||
{
|
||||
var set = sets[i];
|
||||
if (set != null && set.tier == tier)
|
||||
return SanitizeKeywords(set.allowedKeywords);
|
||||
}
|
||||
|
||||
return new string[0];
|
||||
}
|
||||
|
||||
private static string[] GetAllowedPassFeatures(NBShaderFeatureTierPassSet[] sets, NBShaderFeatureTier tier)
|
||||
{
|
||||
if (sets == null || sets.Length == 0)
|
||||
return NBShaderPassFeatureCatalog.GetDefaultAllowedPassFeatures(tier);
|
||||
|
||||
for (var i = 0; i < sets.Length; i++)
|
||||
{
|
||||
var set = sets[i];
|
||||
if (set != null && set.tier == tier)
|
||||
return SanitizePassFeatures(set.allowedPassFeatures);
|
||||
}
|
||||
|
||||
return NBShaderPassFeatureCatalog.GetDefaultAllowedPassFeatures(tier);
|
||||
}
|
||||
}
|
||||
|
||||
internal static class NBShaderFeatureLevelPresetLoader
|
||||
{
|
||||
public const string DefaultPresetAssetPath =
|
||||
"Packages/com.xuanxuan.nb.fx/NBShaders2/Editor/FeatureLevel/LevelAssets/NBShaderDefaultFeatureLevels.asset";
|
||||
|
||||
public static NBShaderFeatureTierKeywordSet[] LoadDefaultTierKeywordSets()
|
||||
{
|
||||
var preset = AssetDatabase.LoadAssetAtPath<NBShaderFeatureLevelPreset>(DefaultPresetAssetPath);
|
||||
if (preset != null)
|
||||
return preset.CreateTierKeywordSets();
|
||||
|
||||
Debug.LogWarning(
|
||||
"NBShader default feature level preset was not found at " +
|
||||
DefaultPresetAssetPath +
|
||||
". Falling back to allowing all Catalog keywords for every tier.");
|
||||
return CreateAllowAllTierKeywordSets();
|
||||
}
|
||||
|
||||
public static NBShaderFeatureTierPassSet[] LoadDefaultTierPassSets()
|
||||
{
|
||||
var preset = AssetDatabase.LoadAssetAtPath<NBShaderFeatureLevelPreset>(DefaultPresetAssetPath);
|
||||
if (preset != null)
|
||||
return preset.CreateTierPassSets();
|
||||
|
||||
return CreateAllowAllTierPassSets();
|
||||
}
|
||||
|
||||
public static string[] LoadDefaultAllowedKeywords(NBShaderFeatureTier tier)
|
||||
{
|
||||
var sets = LoadDefaultTierKeywordSets();
|
||||
for (var i = 0; i < sets.Length; i++)
|
||||
{
|
||||
var set = sets[i];
|
||||
if (set != null && set.tier == tier)
|
||||
return set.allowedKeywords ?? new string[0];
|
||||
}
|
||||
|
||||
return new string[0];
|
||||
}
|
||||
|
||||
public static string[] LoadDefaultAllowedPassFeatures(NBShaderFeatureTier tier)
|
||||
{
|
||||
var sets = LoadDefaultTierPassSets();
|
||||
for (var i = 0; i < sets.Length; i++)
|
||||
{
|
||||
var set = sets[i];
|
||||
if (set != null && set.tier == tier)
|
||||
return set.allowedPassFeatures ?? new string[0];
|
||||
}
|
||||
|
||||
return NBShaderPassFeatureCatalog.GetDefaultAllowedPassFeatures(tier);
|
||||
}
|
||||
|
||||
public static bool SaveDefaultTierKeywordSets(NBShaderFeatureTierKeywordSet[] tierKeywordSets)
|
||||
{
|
||||
return SaveDefaultFeatureSets(tierKeywordSets, null);
|
||||
}
|
||||
|
||||
public static bool SaveDefaultFeatureSets(
|
||||
NBShaderFeatureTierKeywordSet[] tierKeywordSets,
|
||||
NBShaderFeatureTierPassSet[] tierPassSets)
|
||||
{
|
||||
try
|
||||
{
|
||||
var preset = AssetDatabase.LoadAssetAtPath<NBShaderFeatureLevelPreset>(DefaultPresetAssetPath);
|
||||
if (preset == null)
|
||||
{
|
||||
preset = ScriptableObject.CreateInstance<NBShaderFeatureLevelPreset>();
|
||||
AssetDatabase.CreateAsset(preset, DefaultPresetAssetPath);
|
||||
}
|
||||
|
||||
preset.SetTierKeywordSets(tierKeywordSets);
|
||||
if (tierPassSets != null)
|
||||
preset.SetTierPassSets(tierPassSets);
|
||||
EditorUtility.SetDirty(preset);
|
||||
AssetDatabase.SaveAssetIfDirty(preset);
|
||||
AssetDatabase.ImportAsset(DefaultPresetAssetPath);
|
||||
return AssetDatabase.LoadAssetAtPath<NBShaderFeatureLevelPreset>(DefaultPresetAssetPath) != null;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Debug.LogError("Failed to save NBShader default feature level preset: " + exception);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static NBShaderFeatureTierKeywordSet[] CreateAllowAllTierKeywordSets()
|
||||
{
|
||||
var result = new NBShaderFeatureTierKeywordSet[4];
|
||||
for (var i = 0; i < result.Length; i++)
|
||||
{
|
||||
result[i] = new NBShaderFeatureTierKeywordSet
|
||||
{
|
||||
tier = (NBShaderFeatureTier)i,
|
||||
allowedKeywords = (string[])NBShaderFeatureLevelCatalog.ManagedKeywords.Clone()
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static NBShaderFeatureTierPassSet[] CreateAllowAllTierPassSets()
|
||||
{
|
||||
var result = new NBShaderFeatureTierPassSet[4];
|
||||
for (var i = 0; i < result.Length; i++)
|
||||
{
|
||||
var tier = (NBShaderFeatureTier)i;
|
||||
result[i] = new NBShaderFeatureTierPassSet
|
||||
{
|
||||
tier = tier,
|
||||
allowedPassFeatures = NBShaderPassFeatureCatalog.GetDefaultAllowedPassFeatures(tier)
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a320954bde0b436381c4aef9f96ac237
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,739 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NBShader;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBShaders2.Editor.FeatureLevel
|
||||
{
|
||||
[FilePath("ProjectSettings/NBShaderFeatureLevels.asset", FilePathAttribute.Location.ProjectFolder)]
|
||||
public sealed class NBShaderFeatureLevelProjectSettings : ScriptableSingleton<NBShaderFeatureLevelProjectSettings>
|
||||
{
|
||||
private static readonly string[] DefaultQualityNames = { "Default" };
|
||||
|
||||
[SerializeField] private NBShaderFeatureTierKeywordSet[] m_TierKeywordSets;
|
||||
[SerializeField] private NBShaderFeatureTierPassSet[] m_TierPassSets;
|
||||
[SerializeField] private NBShaderQualityTierMapping[] m_QualityTierMappings;
|
||||
[SerializeField] private bool m_EnableDebugSymbols;
|
||||
|
||||
[NonSerialized] private bool m_Initialized;
|
||||
[NonSerialized] private HashSet<string>[] m_AllowedKeywordSetCache;
|
||||
[NonSerialized] private bool m_AllowedKeywordSetCacheValid;
|
||||
[NonSerialized] private HashSet<string>[] m_AllowedPassFeatureSetCache;
|
||||
[NonSerialized] private bool m_AllowedPassFeatureSetCacheValid;
|
||||
|
||||
public NBShaderFeatureTierKeywordSet[] tierKeywordSets { get { EnsureInitialized(); InvalidateAllowedKeywordSetCache(); return m_TierKeywordSets; } }
|
||||
public NBShaderFeatureTierPassSet[] tierPassSets { get { EnsureInitialized(); InvalidateAllowedPassFeatureSetCache(); return m_TierPassSets; } }
|
||||
public NBShaderQualityTierMapping[] qualityTierMappings { get { EnsureInitialized(); return m_QualityTierMappings; } }
|
||||
public bool enableDebugSymbols { get { return m_EnableDebugSymbols; } }
|
||||
|
||||
public void EnsureInitialized()
|
||||
{
|
||||
if (m_Initialized &&
|
||||
HasValidTierKeywordSets() &&
|
||||
HasValidTierPassSets() &&
|
||||
HasValidQualityMappings() &&
|
||||
AreQualityMappingsCurrent())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var changed = false;
|
||||
if (m_TierKeywordSets == null || m_TierKeywordSets.Length != 4)
|
||||
{
|
||||
ResetTierKeywordSetsToDefault();
|
||||
changed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
changed |= NormalizeTierKeywordSets();
|
||||
}
|
||||
|
||||
if (m_TierPassSets == null || m_TierPassSets.Length != 4)
|
||||
{
|
||||
ResetTierPassSetsToDefault();
|
||||
changed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
changed |= NormalizeTierPassSets();
|
||||
}
|
||||
|
||||
if (m_QualityTierMappings == null || m_QualityTierMappings.Length == 0)
|
||||
{
|
||||
ResetQualityMappingsToDefault();
|
||||
changed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
changed |= NormalizeQualityMappingsWithCurrentQualityLevels();
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
Save(true);
|
||||
}
|
||||
|
||||
m_Initialized = true;
|
||||
}
|
||||
|
||||
public void ResetTierKeywordSetsToDefault()
|
||||
{
|
||||
m_TierKeywordSets = NBShaderFeatureLevelPresetLoader.LoadDefaultTierKeywordSets();
|
||||
InvalidateAllowedKeywordSetCache();
|
||||
}
|
||||
|
||||
public void ResetTierPassSetsToDefault()
|
||||
{
|
||||
m_TierPassSets = NBShaderFeatureLevelPresetLoader.LoadDefaultTierPassSets();
|
||||
InvalidateAllowedPassFeatureSetCache();
|
||||
}
|
||||
|
||||
public void ResetQualityMappingsToDefault()
|
||||
{
|
||||
var names = QualitySettings.names;
|
||||
if (names == null || names.Length == 0)
|
||||
names = DefaultQualityNames;
|
||||
|
||||
m_QualityTierMappings = new NBShaderQualityTierMapping[names.Length];
|
||||
for (var i = 0; i < names.Length; i++)
|
||||
{
|
||||
m_QualityTierMappings[i] = new NBShaderQualityTierMapping
|
||||
{
|
||||
qualityName = names[i],
|
||||
tier = GuessTierForQualityIndex(i, names.Length)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public HashSet<string> GetAllowedKeywordSet(NBShaderFeatureTier tier)
|
||||
{
|
||||
return new HashSet<string>(GetAllowedKeywordSetForReadOnlyUse(tier), StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
public HashSet<string> GetAllowedPassFeatureSet(NBShaderFeatureTier tier)
|
||||
{
|
||||
return new HashSet<string>(GetAllowedPassFeatureSetForReadOnlyUse(tier), StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
public bool IsKeywordAllowed(NBShaderFeatureTier tier, string keyword)
|
||||
{
|
||||
return GetAllowedKeywordSetForReadOnlyUse(tier).Contains(keyword);
|
||||
}
|
||||
|
||||
public bool IsPassFeatureAllowed(NBShaderFeatureTier tier, string passFeatureId)
|
||||
{
|
||||
return GetAllowedPassFeatureSetForReadOnlyUse(tier).Contains(passFeatureId);
|
||||
}
|
||||
|
||||
internal HashSet<string> GetAllowedKeywordSetForReadOnlyUse(NBShaderFeatureTier tier)
|
||||
{
|
||||
EnsureInitialized();
|
||||
EnsureAllowedKeywordSetCache();
|
||||
return m_AllowedKeywordSetCache[ToTierIndex(tier)];
|
||||
}
|
||||
|
||||
internal HashSet<string> GetAllowedPassFeatureSetForReadOnlyUse(NBShaderFeatureTier tier)
|
||||
{
|
||||
EnsureInitialized();
|
||||
EnsureAllowedPassFeatureSetCache();
|
||||
return m_AllowedPassFeatureSetCache[ToTierIndex(tier)];
|
||||
}
|
||||
|
||||
internal HashSet<string> GetAllowedKeywordSetForBuildInfoNoSave(NBShaderFeatureTier tier)
|
||||
{
|
||||
var existing = FindTierKeywordSet(tier);
|
||||
if (existing != null && existing.allowedKeywords != null)
|
||||
return BuildSanitizedAllowedSet(existing.allowedKeywords);
|
||||
|
||||
return BuildSanitizedAllowedSet(NBShaderFeatureLevelCatalog.ManagedKeywords);
|
||||
}
|
||||
|
||||
internal HashSet<string> GetAllowedPassFeatureSetForBuildInfoNoSave(NBShaderFeatureTier tier)
|
||||
{
|
||||
var existing = FindTierPassSet(tier);
|
||||
if (existing != null && existing.allowedPassFeatures != null)
|
||||
return BuildSanitizedAllowedPassFeatureSet(existing.allowedPassFeatures);
|
||||
|
||||
return BuildSanitizedAllowedPassFeatureSet(NBShaderFeatureLevelCatalog.ManagedPassFeatures);
|
||||
}
|
||||
|
||||
public void SetKeywordAllowed(NBShaderFeatureTier tier, string keyword, bool allowed)
|
||||
{
|
||||
EnsureInitialized();
|
||||
if (!NBShaderFeatureLevelCatalog.IsManagedKeyword(keyword))
|
||||
return;
|
||||
|
||||
NBShaderFeatureTierKeywordSet target = null;
|
||||
for (var i = 0; i < m_TierKeywordSets.Length; i++)
|
||||
{
|
||||
if (m_TierKeywordSets[i] != null && m_TierKeywordSets[i].tier == tier)
|
||||
{
|
||||
target = m_TierKeywordSets[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (target == null)
|
||||
{
|
||||
target = new NBShaderFeatureTierKeywordSet { tier = tier };
|
||||
var index = (int)tier;
|
||||
if (index >= 0 && index < m_TierKeywordSets.Length)
|
||||
m_TierKeywordSets[index] = target;
|
||||
}
|
||||
|
||||
var keywords = BuildSanitizedAllowedSet(target.allowedKeywords);
|
||||
if (allowed)
|
||||
keywords.Add(keyword);
|
||||
else
|
||||
keywords.Remove(keyword);
|
||||
|
||||
target.allowedKeywords = ToCatalogOrderedArray(keywords);
|
||||
InvalidateAllowedKeywordSetCache();
|
||||
}
|
||||
|
||||
public void SetPassFeatureAllowed(NBShaderFeatureTier tier, string passFeatureId, bool allowed)
|
||||
{
|
||||
EnsureInitialized();
|
||||
if (!NBShaderFeatureLevelCatalog.IsManagedPassFeature(passFeatureId))
|
||||
return;
|
||||
|
||||
NBShaderFeatureTierPassSet target = null;
|
||||
for (var i = 0; i < m_TierPassSets.Length; i++)
|
||||
{
|
||||
if (m_TierPassSets[i] != null && m_TierPassSets[i].tier == tier)
|
||||
{
|
||||
target = m_TierPassSets[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (target == null)
|
||||
{
|
||||
target = new NBShaderFeatureTierPassSet { tier = tier };
|
||||
var index = (int)tier;
|
||||
if (index >= 0 && index < m_TierPassSets.Length)
|
||||
m_TierPassSets[index] = target;
|
||||
}
|
||||
|
||||
var passFeatures = BuildSanitizedAllowedPassFeatureSet(target.allowedPassFeatures);
|
||||
if (allowed)
|
||||
passFeatures.Add(passFeatureId);
|
||||
else
|
||||
passFeatures.Remove(passFeatureId);
|
||||
|
||||
target.allowedPassFeatures = ToCatalogOrderedPassFeatureArray(passFeatures);
|
||||
InvalidateAllowedPassFeatureSetCache();
|
||||
}
|
||||
|
||||
public bool TryGetTierForQualityName(string qualityName, out NBShaderFeatureTier tier)
|
||||
{
|
||||
EnsureInitialized();
|
||||
tier = NBShaderFeatureTier.Ultra;
|
||||
return TryGetTierForQualityNameNoSave(qualityName, out tier);
|
||||
}
|
||||
|
||||
internal bool TryGetTierForQualityNameNoSave(string qualityName, out NBShaderFeatureTier tier)
|
||||
{
|
||||
tier = NBShaderFeatureTier.Ultra;
|
||||
if (string.IsNullOrEmpty(qualityName))
|
||||
return false;
|
||||
|
||||
if (m_QualityTierMappings != null)
|
||||
{
|
||||
for (var i = 0; i < m_QualityTierMappings.Length; i++)
|
||||
{
|
||||
var mapping = m_QualityTierMappings[i];
|
||||
if (mapping == null || !string.Equals(mapping.qualityName, qualityName, StringComparison.Ordinal))
|
||||
continue;
|
||||
|
||||
tier = mapping.tier;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
var names = QualitySettings.names;
|
||||
if (names == null || names.Length == 0)
|
||||
names = DefaultQualityNames;
|
||||
|
||||
for (var i = 0; i < names.Length; i++)
|
||||
{
|
||||
if (!string.Equals(names[i], qualityName, StringComparison.Ordinal))
|
||||
continue;
|
||||
|
||||
tier = GuessTierForQualityIndex(i, names.Length);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public string[] GetQualityNamesForTier(NBShaderFeatureTier tier)
|
||||
{
|
||||
EnsureInitialized();
|
||||
if (m_QualityTierMappings == null || m_QualityTierMappings.Length == 0)
|
||||
return new string[0];
|
||||
|
||||
var names = new List<string>();
|
||||
for (var i = 0; i < m_QualityTierMappings.Length; i++)
|
||||
{
|
||||
var mapping = m_QualityTierMappings[i];
|
||||
if (mapping != null && mapping.tier == tier && !string.IsNullOrEmpty(mapping.qualityName))
|
||||
names.Add(mapping.qualityName);
|
||||
}
|
||||
|
||||
return names.ToArray();
|
||||
}
|
||||
|
||||
public void MoveQualityToTier(string qualityName, NBShaderFeatureTier tier)
|
||||
{
|
||||
EnsureInitialized();
|
||||
if (string.IsNullOrEmpty(qualityName))
|
||||
return;
|
||||
|
||||
for (var i = 0; i < m_QualityTierMappings.Length; i++)
|
||||
{
|
||||
var mapping = m_QualityTierMappings[i];
|
||||
if (mapping == null || !string.Equals(mapping.qualityName, qualityName, StringComparison.Ordinal))
|
||||
continue;
|
||||
|
||||
mapping.tier = tier;
|
||||
return;
|
||||
}
|
||||
|
||||
var newMappings = new NBShaderQualityTierMapping[m_QualityTierMappings.Length + 1];
|
||||
Array.Copy(m_QualityTierMappings, newMappings, m_QualityTierMappings.Length);
|
||||
newMappings[newMappings.Length - 1] = new NBShaderQualityTierMapping
|
||||
{
|
||||
qualityName = qualityName,
|
||||
tier = tier
|
||||
};
|
||||
m_QualityTierMappings = newMappings;
|
||||
}
|
||||
|
||||
public void SaveProjectSettings()
|
||||
{
|
||||
EnsureInitialized();
|
||||
Save(true);
|
||||
}
|
||||
|
||||
public void SetDebugSymbolsEnabled(bool enabled)
|
||||
{
|
||||
m_EnableDebugSymbols = enabled;
|
||||
}
|
||||
|
||||
public void SaveDebugSymbolsProjectSettings()
|
||||
{
|
||||
Save(true);
|
||||
}
|
||||
|
||||
private static NBShaderFeatureTier GuessTierForQualityIndex(int index, int count)
|
||||
{
|
||||
if (count <= 1)
|
||||
return NBShaderFeatureTier.Ultra;
|
||||
var normalized = index / (float)(count - 1);
|
||||
if (normalized < 0.25f) return NBShaderFeatureTier.Low;
|
||||
if (normalized < 0.50f) return NBShaderFeatureTier.Medium;
|
||||
if (normalized < 0.75f) return NBShaderFeatureTier.High;
|
||||
return NBShaderFeatureTier.Ultra;
|
||||
}
|
||||
|
||||
private bool NormalizeTierKeywordSets()
|
||||
{
|
||||
var changed = false;
|
||||
NBShaderFeatureTierKeywordSet[] defaults = null;
|
||||
var normalized = new NBShaderFeatureTierKeywordSet[4];
|
||||
for (var tierIndex = 0; tierIndex < normalized.Length; tierIndex++)
|
||||
{
|
||||
var tier = (NBShaderFeatureTier)tierIndex;
|
||||
var existing = FindTierKeywordSet(tier);
|
||||
if (existing == null)
|
||||
{
|
||||
if (defaults == null)
|
||||
defaults = NBShaderFeatureLevelPresetLoader.LoadDefaultTierKeywordSets();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
normalized[tierIndex] = new NBShaderFeatureTierKeywordSet
|
||||
{
|
||||
tier = tier,
|
||||
allowedKeywords = existing != null
|
||||
? ToCatalogOrderedArray(BuildSanitizedAllowedSet(existing.allowedKeywords))
|
||||
: GetAllowedKeywords(defaults, tier)
|
||||
};
|
||||
|
||||
if (existing != null && !AreKeywordArraysEquivalent(existing.allowedKeywords, normalized[tierIndex].allowedKeywords))
|
||||
changed = true;
|
||||
}
|
||||
|
||||
m_TierKeywordSets = normalized;
|
||||
if (changed)
|
||||
InvalidateAllowedKeywordSetCache();
|
||||
return changed;
|
||||
}
|
||||
|
||||
private bool NormalizeTierPassSets()
|
||||
{
|
||||
var changed = false;
|
||||
NBShaderFeatureTierPassSet[] defaults = null;
|
||||
var normalized = new NBShaderFeatureTierPassSet[4];
|
||||
for (var tierIndex = 0; tierIndex < normalized.Length; tierIndex++)
|
||||
{
|
||||
var tier = (NBShaderFeatureTier)tierIndex;
|
||||
var existing = FindTierPassSet(tier);
|
||||
if (existing == null)
|
||||
{
|
||||
if (defaults == null)
|
||||
defaults = NBShaderFeatureLevelPresetLoader.LoadDefaultTierPassSets();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
normalized[tierIndex] = new NBShaderFeatureTierPassSet
|
||||
{
|
||||
tier = tier,
|
||||
allowedPassFeatures = existing != null
|
||||
? ToCatalogOrderedPassFeatureArray(BuildSanitizedAllowedPassFeatureSet(existing.allowedPassFeatures))
|
||||
: GetAllowedPassFeatures(defaults, tier)
|
||||
};
|
||||
|
||||
if (existing != null && !ArePassFeatureArraysEquivalent(existing.allowedPassFeatures, normalized[tierIndex].allowedPassFeatures))
|
||||
changed = true;
|
||||
}
|
||||
|
||||
m_TierPassSets = normalized;
|
||||
if (changed)
|
||||
InvalidateAllowedPassFeatureSetCache();
|
||||
return changed;
|
||||
}
|
||||
|
||||
private bool HasValidTierKeywordSets()
|
||||
{
|
||||
if (m_TierKeywordSets == null || m_TierKeywordSets.Length != 4)
|
||||
return false;
|
||||
|
||||
for (var i = 0; i < m_TierKeywordSets.Length; i++)
|
||||
{
|
||||
var set = m_TierKeywordSets[i];
|
||||
if (set == null || set.tier != (NBShaderFeatureTier)i || set.allowedKeywords == null)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool HasValidTierPassSets()
|
||||
{
|
||||
if (m_TierPassSets == null || m_TierPassSets.Length != 4)
|
||||
return false;
|
||||
|
||||
for (var i = 0; i < m_TierPassSets.Length; i++)
|
||||
{
|
||||
var set = m_TierPassSets[i];
|
||||
if (set == null || set.tier != (NBShaderFeatureTier)i || set.allowedPassFeatures == null)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool HasValidQualityMappings()
|
||||
{
|
||||
return m_QualityTierMappings != null && m_QualityTierMappings.Length > 0;
|
||||
}
|
||||
|
||||
private bool AreQualityMappingsCurrent()
|
||||
{
|
||||
var names = QualitySettings.names;
|
||||
if (names == null || names.Length == 0)
|
||||
names = DefaultQualityNames;
|
||||
|
||||
if (m_QualityTierMappings == null || m_QualityTierMappings.Length != names.Length)
|
||||
return false;
|
||||
|
||||
for (var i = 0; i < names.Length; i++)
|
||||
{
|
||||
var mapping = m_QualityTierMappings[i];
|
||||
if (mapping == null || !string.Equals(mapping.qualityName, names[i], StringComparison.Ordinal))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void EnsureAllowedKeywordSetCache()
|
||||
{
|
||||
if (m_AllowedKeywordSetCacheValid &&
|
||||
m_AllowedKeywordSetCache != null &&
|
||||
m_AllowedKeywordSetCache.Length == 4)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_AllowedKeywordSetCache == null || m_AllowedKeywordSetCache.Length != 4)
|
||||
m_AllowedKeywordSetCache = new HashSet<string>[4];
|
||||
|
||||
for (var i = 0; i < m_AllowedKeywordSetCache.Length; i++)
|
||||
m_AllowedKeywordSetCache[i] = BuildAllowedKeywordSet((NBShaderFeatureTier)i);
|
||||
|
||||
m_AllowedKeywordSetCacheValid = true;
|
||||
}
|
||||
|
||||
private void EnsureAllowedPassFeatureSetCache()
|
||||
{
|
||||
if (m_AllowedPassFeatureSetCacheValid &&
|
||||
m_AllowedPassFeatureSetCache != null &&
|
||||
m_AllowedPassFeatureSetCache.Length == 4)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_AllowedPassFeatureSetCache == null || m_AllowedPassFeatureSetCache.Length != 4)
|
||||
m_AllowedPassFeatureSetCache = new HashSet<string>[4];
|
||||
|
||||
for (var i = 0; i < m_AllowedPassFeatureSetCache.Length; i++)
|
||||
m_AllowedPassFeatureSetCache[i] = BuildAllowedPassFeatureSet((NBShaderFeatureTier)i);
|
||||
|
||||
m_AllowedPassFeatureSetCacheValid = true;
|
||||
}
|
||||
|
||||
private void InvalidateAllowedKeywordSetCache()
|
||||
{
|
||||
m_AllowedKeywordSetCacheValid = false;
|
||||
}
|
||||
|
||||
private void InvalidateAllowedPassFeatureSetCache()
|
||||
{
|
||||
m_AllowedPassFeatureSetCacheValid = false;
|
||||
}
|
||||
|
||||
private HashSet<string> BuildAllowedKeywordSet(NBShaderFeatureTier tier)
|
||||
{
|
||||
var result = new HashSet<string>(StringComparer.Ordinal);
|
||||
if (m_TierKeywordSets == null)
|
||||
return result;
|
||||
|
||||
for (var i = 0; i < m_TierKeywordSets.Length; i++)
|
||||
{
|
||||
var set = m_TierKeywordSets[i];
|
||||
if (set == null || set.tier != tier || set.allowedKeywords == null)
|
||||
continue;
|
||||
|
||||
for (var k = 0; k < set.allowedKeywords.Length; k++)
|
||||
{
|
||||
var keyword = set.allowedKeywords[k];
|
||||
if (NBShaderFeatureLevelCatalog.IsManagedKeyword(keyword))
|
||||
result.Add(keyword);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private HashSet<string> BuildAllowedPassFeatureSet(NBShaderFeatureTier tier)
|
||||
{
|
||||
var result = new HashSet<string>(StringComparer.Ordinal);
|
||||
if (m_TierPassSets == null)
|
||||
return result;
|
||||
|
||||
for (var i = 0; i < m_TierPassSets.Length; i++)
|
||||
{
|
||||
var set = m_TierPassSets[i];
|
||||
if (set == null || set.tier != tier || set.allowedPassFeatures == null)
|
||||
continue;
|
||||
|
||||
for (var k = 0; k < set.allowedPassFeatures.Length; k++)
|
||||
{
|
||||
var passFeature = set.allowedPassFeatures[k];
|
||||
if (NBShaderFeatureLevelCatalog.IsManagedPassFeature(passFeature))
|
||||
result.Add(passFeature);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int ToTierIndex(NBShaderFeatureTier tier)
|
||||
{
|
||||
var index = (int)tier;
|
||||
return index >= 0 && index < 4 ? index : (int)NBShaderFeatureTier.Ultra;
|
||||
}
|
||||
|
||||
private NBShaderFeatureTierKeywordSet FindTierKeywordSet(NBShaderFeatureTier tier)
|
||||
{
|
||||
if (m_TierKeywordSets == null)
|
||||
return null;
|
||||
|
||||
for (var i = 0; i < m_TierKeywordSets.Length; i++)
|
||||
{
|
||||
var set = m_TierKeywordSets[i];
|
||||
if (set != null && set.tier == tier)
|
||||
return set;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private NBShaderFeatureTierPassSet FindTierPassSet(NBShaderFeatureTier tier)
|
||||
{
|
||||
if (m_TierPassSets == null)
|
||||
return null;
|
||||
|
||||
for (var i = 0; i < m_TierPassSets.Length; i++)
|
||||
{
|
||||
var set = m_TierPassSets[i];
|
||||
if (set != null && set.tier == tier)
|
||||
return set;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool NormalizeQualityMappingsWithCurrentQualityLevels()
|
||||
{
|
||||
var names = QualitySettings.names;
|
||||
if (names == null || names.Length == 0)
|
||||
names = DefaultQualityNames;
|
||||
|
||||
var previous = new Dictionary<string, NBShaderFeatureTier>(StringComparer.Ordinal);
|
||||
if (m_QualityTierMappings != null)
|
||||
{
|
||||
for (var i = 0; i < m_QualityTierMappings.Length; i++)
|
||||
{
|
||||
var mapping = m_QualityTierMappings[i];
|
||||
if (mapping == null || string.IsNullOrEmpty(mapping.qualityName) || previous.ContainsKey(mapping.qualityName))
|
||||
continue;
|
||||
|
||||
previous.Add(mapping.qualityName, mapping.tier);
|
||||
}
|
||||
}
|
||||
|
||||
var changed = m_QualityTierMappings == null || m_QualityTierMappings.Length != names.Length;
|
||||
m_QualityTierMappings = new NBShaderQualityTierMapping[names.Length];
|
||||
for (var i = 0; i < names.Length; i++)
|
||||
{
|
||||
NBShaderFeatureTier tier;
|
||||
if (!previous.TryGetValue(names[i], out tier))
|
||||
{
|
||||
tier = GuessTierForQualityIndex(i, names.Length);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
m_QualityTierMappings[i] = new NBShaderQualityTierMapping
|
||||
{
|
||||
qualityName = names[i],
|
||||
tier = tier
|
||||
};
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
private static HashSet<string> BuildSanitizedAllowedSet(string[] keywords)
|
||||
{
|
||||
var result = new HashSet<string>(StringComparer.Ordinal);
|
||||
if (keywords == null)
|
||||
return result;
|
||||
|
||||
for (var i = 0; i < keywords.Length; i++)
|
||||
{
|
||||
var keyword = keywords[i];
|
||||
if (NBShaderFeatureLevelCatalog.IsManagedKeyword(keyword))
|
||||
result.Add(keyword);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static HashSet<string> BuildSanitizedAllowedPassFeatureSet(string[] passFeatures)
|
||||
{
|
||||
var result = new HashSet<string>(StringComparer.Ordinal);
|
||||
if (passFeatures == null)
|
||||
return result;
|
||||
|
||||
for (var i = 0; i < passFeatures.Length; i++)
|
||||
{
|
||||
var passFeature = passFeatures[i];
|
||||
if (NBShaderFeatureLevelCatalog.IsManagedPassFeature(passFeature))
|
||||
result.Add(passFeature);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static string[] ToCatalogOrderedArray(HashSet<string> keywords)
|
||||
{
|
||||
if (keywords == null || keywords.Count == 0)
|
||||
return new string[0];
|
||||
|
||||
var result = new List<string>();
|
||||
var catalog = NBShaderFeatureLevelCatalog.ManagedKeywords;
|
||||
for (var i = 0; i < catalog.Length; i++)
|
||||
{
|
||||
if (keywords.Contains(catalog[i]))
|
||||
result.Add(catalog[i]);
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
private static string[] ToCatalogOrderedPassFeatureArray(HashSet<string> passFeatures)
|
||||
{
|
||||
if (passFeatures == null || passFeatures.Count == 0)
|
||||
return new string[0];
|
||||
|
||||
var result = new List<string>();
|
||||
var catalog = NBShaderFeatureLevelCatalog.ManagedPassFeatures;
|
||||
for (var i = 0; i < catalog.Length; i++)
|
||||
{
|
||||
if (passFeatures.Contains(catalog[i]))
|
||||
result.Add(catalog[i]);
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
private static string[] GetAllowedKeywords(NBShaderFeatureTierKeywordSet[] sets, NBShaderFeatureTier tier)
|
||||
{
|
||||
if (sets == null)
|
||||
return new string[0];
|
||||
|
||||
for (var i = 0; i < sets.Length; i++)
|
||||
{
|
||||
var set = sets[i];
|
||||
if (set != null && set.tier == tier)
|
||||
return set.allowedKeywords ?? new string[0];
|
||||
}
|
||||
|
||||
return new string[0];
|
||||
}
|
||||
|
||||
private static string[] GetAllowedPassFeatures(NBShaderFeatureTierPassSet[] sets, NBShaderFeatureTier tier)
|
||||
{
|
||||
if (sets == null)
|
||||
return new string[0];
|
||||
|
||||
for (var i = 0; i < sets.Length; i++)
|
||||
{
|
||||
var set = sets[i];
|
||||
if (set != null && set.tier == tier)
|
||||
return set.allowedPassFeatures ?? new string[0];
|
||||
}
|
||||
|
||||
return new string[0];
|
||||
}
|
||||
|
||||
private static bool AreKeywordArraysEquivalent(string[] a, string[] b)
|
||||
{
|
||||
var setA = BuildSanitizedAllowedSet(a);
|
||||
var setB = BuildSanitizedAllowedSet(b);
|
||||
return setA.SetEquals(setB);
|
||||
}
|
||||
|
||||
private static bool ArePassFeatureArraysEquivalent(string[] a, string[] b)
|
||||
{
|
||||
var setA = BuildSanitizedAllowedPassFeatureSet(a);
|
||||
var setB = BuildSanitizedAllowedPassFeatureSet(b);
|
||||
return setA.SetEquals(setB);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ac3a7eef1342b4895b4c1a6bf2669486
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,247 @@
|
||||
using System.Collections.Generic;
|
||||
using NBShader;
|
||||
|
||||
namespace NBShaders2.Editor.FeatureLevel
|
||||
{
|
||||
internal enum NBShaderFeatureLevelRowKind
|
||||
{
|
||||
Group,
|
||||
Keyword,
|
||||
Pass
|
||||
}
|
||||
|
||||
internal enum NBShaderFeaturePerformanceCost
|
||||
{
|
||||
Low,
|
||||
Medium,
|
||||
High,
|
||||
Ultra
|
||||
}
|
||||
|
||||
internal sealed class NBShaderFeatureLevelRow
|
||||
{
|
||||
public readonly NBShaderFeatureLevelRowKind kind;
|
||||
public readonly string key;
|
||||
public readonly string parentKey;
|
||||
public readonly string keyword;
|
||||
public readonly string passFeatureId;
|
||||
public readonly string labelFallback;
|
||||
public readonly NBShaderFeaturePerformanceCost performanceCost;
|
||||
public readonly string effectFallback;
|
||||
public readonly int depth;
|
||||
|
||||
public NBShaderFeatureLevelRow(
|
||||
NBShaderFeatureLevelRowKind kind,
|
||||
string key,
|
||||
string parentKey,
|
||||
string keyword,
|
||||
string passFeatureId,
|
||||
string labelFallback,
|
||||
NBShaderFeaturePerformanceCost performanceCost,
|
||||
string effectFallback,
|
||||
int depth)
|
||||
{
|
||||
this.kind = kind;
|
||||
this.key = key;
|
||||
this.parentKey = parentKey;
|
||||
this.keyword = keyword;
|
||||
this.passFeatureId = passFeatureId;
|
||||
this.labelFallback = labelFallback;
|
||||
this.performanceCost = performanceCost;
|
||||
this.effectFallback = effectFallback;
|
||||
this.depth = depth;
|
||||
}
|
||||
|
||||
public bool isKeyword
|
||||
{
|
||||
get { return kind == NBShaderFeatureLevelRowKind.Keyword; }
|
||||
}
|
||||
|
||||
public bool isPass
|
||||
{
|
||||
get { return kind == NBShaderFeatureLevelRowKind.Pass; }
|
||||
}
|
||||
}
|
||||
|
||||
internal static class NBShaderFeatureLevelRowCatalog
|
||||
{
|
||||
public static readonly NBShaderFeatureLevelRow[] Rows =
|
||||
{
|
||||
Group("mode", null, "Mode", 0),
|
||||
Group("transparent", "mode", "Transparent Mode", 1),
|
||||
Keyword("_ALPHATEST_ON", "transparent", "Alpha Test", 2, NBShaderFeaturePerformanceCost.Medium, "Cuts pixels by alpha threshold."),
|
||||
Keyword("_ALPHAPREMULTIPLY_ON", "transparent", "Premultiply Alpha", 2, NBShaderFeaturePerformanceCost.Low, "Uses premultiplied alpha blending."),
|
||||
Keyword("_ALPHAMODULATE_ON", "transparent", "Alpha Modulate", 2, NBShaderFeaturePerformanceCost.Low, "Uses alpha modulation transparent blending."),
|
||||
Group("time", "mode", "Time Mode", 1),
|
||||
Keyword("_UNSCALETIME", "time", "Unscaled Time", 2, NBShaderFeaturePerformanceCost.Low, "Uses unscaled time."),
|
||||
Keyword("_SCRIPTABLETIME", "time", "Scriptable Time", 2, NBShaderFeaturePerformanceCost.Low, "Uses script provided time value."),
|
||||
|
||||
Group("base", null, "Base", 0),
|
||||
Keyword("_DISTANCE_FADE", "base", "Distance Fade", 1, NBShaderFeaturePerformanceCost.Low, "Fades material by camera distance."),
|
||||
Keyword("_SOFTPARTICLES_ON", "base", "Soft Particles", 1, NBShaderFeaturePerformanceCost.High, "Softens particles using scene depth."),
|
||||
Keyword("_STENCIL_WITHOUT_PLAYER", "base", "Stencil Without Player", 1, NBShaderFeaturePerformanceCost.Low, "Uses stencil without player mask."),
|
||||
Group("particle", "base", "Particle Data", 1),
|
||||
Keyword("_PARCUSTOMDATA_ON", "particle", "Particle Custom Data", 2, NBShaderFeaturePerformanceCost.Low, "Reads particle custom data channels."),
|
||||
|
||||
Group("light", null, "Lighting", 0),
|
||||
Group("lightMode", "light", "Light Mode", 1),
|
||||
Keyword("_FX_LIGHT_MODE_UNLIT", "lightMode", "Unlit Lighting", 2, NBShaderFeaturePerformanceCost.Low, "Uses unlit shading."),
|
||||
Keyword("_FX_LIGHT_MODE_BLINN_PHONG", "lightMode", "Blinn-Phong Lighting", 2, NBShaderFeaturePerformanceCost.Medium, "Uses Blinn Phong lighting."),
|
||||
Keyword("_FX_LIGHT_MODE_HALF_LAMBERT", "lightMode", "Half Lambert Lighting", 2, NBShaderFeaturePerformanceCost.Medium, "Uses half Lambert lighting."),
|
||||
Keyword("_FX_LIGHT_MODE_PBR", "lightMode", "PBR Lighting", 2, NBShaderFeaturePerformanceCost.High, "Uses PBR lighting calculations."),
|
||||
Keyword("_FX_LIGHT_MODE_SIX_WAY", "lightMode", "Six Way Lighting", 2, NBShaderFeaturePerformanceCost.Ultra, "Uses six way lighting data."),
|
||||
Keyword("_SPECULAR_COLOR", "light", "Specular Color", 1, NBShaderFeaturePerformanceCost.Medium, "Adds specular color highlight."),
|
||||
Keyword("_NORMALMAP", "light", "Normal Map", 1, NBShaderFeaturePerformanceCost.High, "Uses normal map lighting detail."),
|
||||
Keyword("_MATCAP", "light", "MatCap", 1, NBShaderFeaturePerformanceCost.Medium, "Adds matcap lighting texture."),
|
||||
Keyword("VFX_SIX_WAY_ABSORPTION", "light", "Six Way Absorption", 1, NBShaderFeaturePerformanceCost.High, "Adds absorption response for six way lighting."),
|
||||
|
||||
Group("feature", null, "Feature", 0),
|
||||
Keyword("_MASKMAP_ON", "feature", "Mask Map", 1, NBShaderFeaturePerformanceCost.Medium, "Uses the first mask map."),
|
||||
Keyword("_MASKMAP2_ON", "_MASKMAP_ON", "Mask Map 2", 2, NBShaderFeaturePerformanceCost.Medium, "Uses the second mask map."),
|
||||
Keyword("_MASKMAP3_ON", "_MASKMAP_ON", "Mask Map 3", 2, NBShaderFeaturePerformanceCost.Medium, "Uses the third mask map."),
|
||||
Keyword("NB_DEBUG_MASK", "_MASKMAP_ON", "Debug Mask", 2, NBShaderFeaturePerformanceCost.Low, "Shows mask values for debugging."),
|
||||
Keyword("_NOISEMAP", "feature", "Noise Map", 1, NBShaderFeaturePerformanceCost.High, "Uses a noise map for distortion."),
|
||||
Keyword("_NOISE_MASKMAP", "_NOISEMAP", "Noise Mask Map", 2, NBShaderFeaturePerformanceCost.Medium, "Masks the distortion effect."),
|
||||
Keyword("NB_DEBUG_DISTORT", "_NOISEMAP", "Debug Distort", 2, NBShaderFeaturePerformanceCost.Low, "Shows distortion strength for debugging."),
|
||||
Keyword("_SCREEN_DISTORT_MODE", "_NOISEMAP", "Screen Distort", 2, NBShaderFeaturePerformanceCost.Ultra, "Samples screen texture for distortion."),
|
||||
Keyword("_DISTORT_REFRACTION", "_NOISEMAP", "Distort Refraction", 2, NBShaderFeaturePerformanceCost.High, "Uses refraction style distortion."),
|
||||
Keyword("_CHROMATIC_ABERRATION", "feature", "Chromatic Aberration", 1, NBShaderFeaturePerformanceCost.High, "Splits color channels for distortion fringe."),
|
||||
Keyword("_EMISSION", "feature", "Emission", 1, NBShaderFeaturePerformanceCost.Medium, "Adds emissive color or texture contribution."),
|
||||
Keyword("_COLORMAPBLEND", "feature", "Color Map Blend", 1, NBShaderFeaturePerformanceCost.Medium, "Blends an extra color gradient texture."),
|
||||
Keyword("_COLOR_RAMP", "feature", "Color Ramp", 1, NBShaderFeaturePerformanceCost.Medium, "Remaps color through a ramp."),
|
||||
Keyword("_COLOR_RAMP_MAP", "_COLOR_RAMP", "Color Ramp Map", 2, NBShaderFeaturePerformanceCost.Medium, "Uses a texture as the color ramp source."),
|
||||
Keyword("_DISSOLVE", "feature", "Dissolve", 1, NBShaderFeaturePerformanceCost.High, "Clips and blends pixels for dissolve."),
|
||||
Keyword("_DISSOLVE_MASK", "_DISSOLVE", "Dissolve Mask", 2, NBShaderFeaturePerformanceCost.High, "Uses a process mask for dissolve."),
|
||||
Keyword("_DISSOLVE_RAMP", "_DISSOLVE", "Dissolve Ramp", 2, NBShaderFeaturePerformanceCost.Medium, "Adds ramp coloring to dissolve edges."),
|
||||
Keyword("_DISSOLVE_RAMP_MAP", "_DISSOLVE_RAMP", "Dissolve Ramp Map", 3, NBShaderFeaturePerformanceCost.Medium, "Uses a texture for dissolve ramp color."),
|
||||
Keyword("NB_DEBUG_DISSOLVE", "_DISSOLVE", "Debug Dissolve", 2, NBShaderFeaturePerformanceCost.Low, "Shows dissolve values for debugging."),
|
||||
Keyword("_PROGRAM_NOISE", "feature", "Program Noise", 1, NBShaderFeaturePerformanceCost.High, "Generates procedural noise in shader."),
|
||||
Keyword("_PROGRAM_NOISE_SIMPLE", "_PROGRAM_NOISE", "Program Noise Simple", 2, NBShaderFeaturePerformanceCost.High, "Enables simple procedural noise."),
|
||||
Keyword("_PROGRAM_NOISE_VORONOI", "_PROGRAM_NOISE", "Program Noise Voronoi", 2, NBShaderFeaturePerformanceCost.High, "Enables Voronoi procedural noise."),
|
||||
Keyword("NB_DEBUG_PNOISE", "_PROGRAM_NOISE", "Debug Program Noise", 2, NBShaderFeaturePerformanceCost.Low, "Shows procedural noise values for debugging."),
|
||||
Keyword("_SHARED_UV", "feature", "Shared UV", 1, NBShaderFeaturePerformanceCost.Low, "Shares a common UV transform."),
|
||||
Keyword("_FRESNEL", "feature", "Fresnel", 1, NBShaderFeaturePerformanceCost.Medium, "Adds view angle based rim lighting."),
|
||||
Keyword("NB_DEBUG_FRESNEL", "_FRESNEL", "Debug Fresnel", 2, NBShaderFeaturePerformanceCost.Low, "Shows fresnel values for debugging."),
|
||||
Keyword("_VERTEX_OFFSET", "feature", "Vertex Offset", 1, NBShaderFeaturePerformanceCost.High, "Offsets vertices in the shader."),
|
||||
Keyword("_VERTEX_OFFSET_MASKMAP", "_VERTEX_OFFSET", "Vertex Offset Mask Map", 2, NBShaderFeaturePerformanceCost.High, "Masks vertex offset by texture."),
|
||||
Keyword("NB_DEBUG_VERTEX_OFFSET", "_VERTEX_OFFSET", "Debug Vertex Offset", 2, NBShaderFeaturePerformanceCost.Low, "Shows vertex offset values for debugging."),
|
||||
Group("depth", "feature", "Depth", 1),
|
||||
Keyword("_DEPTH_DECAL", "depth", "Depth Decal", 2, NBShaderFeaturePerformanceCost.High, "Projects decal effect using scene depth."),
|
||||
Keyword("_DEPTH_OUTLINE", "depth", "Depth Outline", 2, NBShaderFeaturePerformanceCost.High, "Creates outline effect using depth difference."),
|
||||
Keyword("_OVERRIDE_Z", "depth", "Override Z", 2, NBShaderFeaturePerformanceCost.Medium, "Writes a fixed camera eye depth through SV_Depth."),
|
||||
Keyword("_PARALLAX_MAPPING", "feature", "Parallax Mapping", 1, NBShaderFeaturePerformanceCost.High, "Offsets UVs for parallax depth."),
|
||||
Keyword("_FLIPBOOKBLENDING_ON", "feature", "Flipbook Blending", 1, NBShaderFeaturePerformanceCost.Medium, "Blends between flipbook frames."),
|
||||
Keyword("_VAT", "feature", "VAT", 1, NBShaderFeaturePerformanceCost.Ultra, "Enables vertex animation texture playback."),
|
||||
Group("vatMode", "_VAT", "VAT Mode", 2),
|
||||
Keyword("_VAT_HOUDINI", "vatMode", "Houdini VAT", 3, NBShaderFeaturePerformanceCost.Ultra, "Uses Houdini VAT data layout."),
|
||||
Keyword("_VAT_TYFLOW", "vatMode", "TyFlow VAT", 3, NBShaderFeaturePerformanceCost.Ultra, "Uses TyFlow VAT data layout."),
|
||||
Group("houdiniVat", "_VAT", "Houdini VAT", 2),
|
||||
Keyword("_HOUDINI_VAT_SOFTBODY", "houdiniVat", "Houdini VAT Soft Body", 3, NBShaderFeaturePerformanceCost.Ultra, "Uses Houdini soft body VAT mode."),
|
||||
Keyword("_HOUDINI_VAT_RIGIDBODY", "houdiniVat", "Houdini VAT Rigid Body", 3, NBShaderFeaturePerformanceCost.Ultra, "Uses Houdini rigid body VAT mode."),
|
||||
Keyword("_HOUDINI_VAT_DYNAMIC_REMESH", "houdiniVat", "Houdini VAT Dynamic Remesh", 3, NBShaderFeaturePerformanceCost.Ultra, "Uses Houdini dynamic remesh VAT mode."),
|
||||
Keyword("_HOUDINI_VAT_PARTICLE_SPRITE", "houdiniVat", "Houdini VAT Particle Sprite", 3, NBShaderFeaturePerformanceCost.Ultra, "Uses Houdini particle sprite VAT mode."),
|
||||
Group("tyflowVat", "_VAT", "TyFlow VAT", 2),
|
||||
Keyword("_TYFLOW_VAT_ABSOLUTE", "tyflowVat", "TyFlow VAT Absolute", 3, NBShaderFeaturePerformanceCost.Ultra, "Uses TyFlow absolute VAT mode."),
|
||||
Keyword("_TYFLOW_VAT_RELATIVE", "tyflowVat", "TyFlow VAT Relative", 3, NBShaderFeaturePerformanceCost.Ultra, "Uses TyFlow relative VAT mode."),
|
||||
Keyword("_TYFLOW_VAT_SKIN_R", "tyflowVat", "TyFlow VAT Skin R", 3, NBShaderFeaturePerformanceCost.Ultra, "Uses TyFlow skin R VAT mode."),
|
||||
Keyword("_TYFLOW_VAT_SKIN_PR", "tyflowVat", "TyFlow VAT Skin PR", 3, NBShaderFeaturePerformanceCost.Ultra, "Uses TyFlow skin PR VAT mode."),
|
||||
Keyword("_TYFLOW_VAT_SKIN_PRSAVE", "tyflowVat", "TyFlow VAT Skin PR Save", 3, NBShaderFeaturePerformanceCost.Ultra, "Uses TyFlow skin PR save VAT mode."),
|
||||
Keyword("_TYFLOW_VAT_SKIN_PRSXYZ", "tyflowVat", "TyFlow VAT Skin PRSXYZ", 3, NBShaderFeaturePerformanceCost.Ultra, "Uses TyFlow skin PRSXYZ VAT mode."),
|
||||
|
||||
Group("passes", null, "Passes", 0),
|
||||
Pass(NBShaderPassFeatureCatalog.BackFirstPassId, "passes", "Back First Pass", 1, NBShaderFeaturePerformanceCost.Medium, "Renders the optional back-face pre-pass."),
|
||||
Pass(NBShaderPassFeatureCatalog.CameraOpaqueDistortPassId, "passes", "Camera Opaque Distort Pass", 1, NBShaderFeaturePerformanceCost.Ultra, "Renders the camera opaque texture screen distort pass."),
|
||||
Pass(NBShaderPassFeatureCatalog.DeferredDistortPassId, "passes", "Deferred Distort Pass", 1, NBShaderFeaturePerformanceCost.Ultra, "Renders the deferred screen distort pass."),
|
||||
Pass(NBShaderPassFeatureCatalog.DepthOnlyPassId, "passes", "Depth Only Pass", 1, NBShaderFeaturePerformanceCost.Medium, "Writes material depth for URP depth prepass usage."),
|
||||
Pass(NBShaderPassFeatureCatalog.ShadowCasterPassId, "passes", "Shadow Caster Pass", 1, NBShaderFeaturePerformanceCost.High, "Renders the material into shadow maps."),
|
||||
Pass(NBShaderPassFeatureCatalog.Universal2DPassId, "passes", "Universal 2D Pass", 1, NBShaderFeaturePerformanceCost.Medium, "Allows the material to render through URP 2D renderer.")
|
||||
};
|
||||
|
||||
private static readonly HashSet<string> ParentKeys = BuildParentKeys();
|
||||
private static readonly Dictionary<string, NBShaderFeatureLevelRow> RowsByKey = BuildRowsByKey();
|
||||
|
||||
public static bool HasChildren(NBShaderFeatureLevelRow row)
|
||||
{
|
||||
return row != null && ParentKeys.Contains(row.key);
|
||||
}
|
||||
|
||||
public static bool TryGetRow(string key, out NBShaderFeatureLevelRow row)
|
||||
{
|
||||
return RowsByKey.TryGetValue(key, out row);
|
||||
}
|
||||
|
||||
private static NBShaderFeatureLevelRow Group(string key, string parentKey, string labelFallback, int depth)
|
||||
{
|
||||
return new NBShaderFeatureLevelRow(
|
||||
NBShaderFeatureLevelRowKind.Group,
|
||||
key,
|
||||
parentKey,
|
||||
null,
|
||||
null,
|
||||
labelFallback,
|
||||
NBShaderFeaturePerformanceCost.Low,
|
||||
null,
|
||||
depth);
|
||||
}
|
||||
|
||||
private static NBShaderFeatureLevelRow Keyword(
|
||||
string keyword,
|
||||
string parentKey,
|
||||
string labelFallback,
|
||||
int depth,
|
||||
NBShaderFeaturePerformanceCost performanceCost,
|
||||
string effectFallback)
|
||||
{
|
||||
return new NBShaderFeatureLevelRow(
|
||||
NBShaderFeatureLevelRowKind.Keyword,
|
||||
keyword,
|
||||
parentKey,
|
||||
keyword,
|
||||
null,
|
||||
labelFallback,
|
||||
performanceCost,
|
||||
effectFallback,
|
||||
depth);
|
||||
}
|
||||
|
||||
private static NBShaderFeatureLevelRow Pass(
|
||||
string passFeatureId,
|
||||
string parentKey,
|
||||
string labelFallback,
|
||||
int depth,
|
||||
NBShaderFeaturePerformanceCost performanceCost,
|
||||
string effectFallback)
|
||||
{
|
||||
return new NBShaderFeatureLevelRow(
|
||||
NBShaderFeatureLevelRowKind.Pass,
|
||||
passFeatureId,
|
||||
parentKey,
|
||||
null,
|
||||
passFeatureId,
|
||||
labelFallback,
|
||||
performanceCost,
|
||||
effectFallback,
|
||||
depth);
|
||||
}
|
||||
|
||||
private static HashSet<string> BuildParentKeys()
|
||||
{
|
||||
var result = new HashSet<string>();
|
||||
for (var i = 0; i < Rows.Length; i++)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Rows[i].parentKey))
|
||||
result.Add(Rows[i].parentKey);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Dictionary<string, NBShaderFeatureLevelRow> BuildRowsByKey()
|
||||
{
|
||||
var result = new Dictionary<string, NBShaderFeatureLevelRow>();
|
||||
for (var i = 0; i < Rows.Length; i++)
|
||||
result[Rows[i].key] = Rows[i];
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2abec81852044be281de893c9ef6ea3b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,881 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using NBShader;
|
||||
using NBShaderEditor;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBShaders2.Editor.FeatureLevel
|
||||
{
|
||||
[InitializeOnLoad]
|
||||
public static class NBShaderFeatureLevelSettingsProvider
|
||||
{
|
||||
internal const string SettingsPath = NBFXProjectSettings.SettingsPath;
|
||||
private const float FeatureColumnWidth = 240f;
|
||||
private const float TierColumnWidth = 92f;
|
||||
private const float CostColumnWidth = 84f;
|
||||
private const float EffectColumnWidth = 280f;
|
||||
private const float DescriptionColumnWidth = 230f;
|
||||
private const float RowHeight = 22f;
|
||||
private const float HeaderHeight = 24f;
|
||||
private const float TableMinHeight = 360f;
|
||||
private const float TableMaxHeight = 640f;
|
||||
private const float TableWidth = FeatureColumnWidth + TierColumnWidth * 4f + CostColumnWidth + EffectColumnWidth + DescriptionColumnWidth;
|
||||
private const float RowIndentWidth = 14f;
|
||||
private const string FoldoutSessionPrefix = "NBShaderFeatureLevelSettingsProvider.Foldout.";
|
||||
|
||||
private sealed class RowContentCache
|
||||
{
|
||||
public GUIContent rowContent;
|
||||
public GUIContent effectContent;
|
||||
public GUIContent descriptionContent;
|
||||
}
|
||||
|
||||
private static readonly NBShaderFeatureTier[] Tiers =
|
||||
{
|
||||
NBShaderFeatureTier.Low,
|
||||
NBShaderFeatureTier.Medium,
|
||||
NBShaderFeatureTier.High,
|
||||
NBShaderFeatureTier.Ultra
|
||||
};
|
||||
|
||||
private static readonly Dictionary<string, RowContentCache> s_RowContentCache =
|
||||
new Dictionary<string, RowContentCache>(StringComparer.Ordinal);
|
||||
private static readonly Dictionary<NBShaderFeaturePerformanceCost, GUIContent> s_CostContentCache =
|
||||
new Dictionary<NBShaderFeaturePerformanceCost, GUIContent>();
|
||||
private static readonly GUIContent[] s_QualitySummaryContents = new GUIContent[Tiers.Length];
|
||||
private static readonly string[] s_QualitySummaryTexts = new string[Tiers.Length];
|
||||
private static readonly StringBuilder s_QualitySummaryBuilder = new StringBuilder(128);
|
||||
|
||||
private static Vector2 s_TableScrollPosition;
|
||||
private static string s_ContentCacheLanguage;
|
||||
private static GUIContent s_ProviderLabelContent;
|
||||
private static GUIContent s_QualityDescriptionContent;
|
||||
private static GUIContent s_NoValueContent;
|
||||
private static string s_QualityMenuTipText;
|
||||
private static int s_QualitySummaryHash;
|
||||
|
||||
static NBShaderFeatureLevelSettingsProvider()
|
||||
{
|
||||
NBFXProjectSettings.RegisterSettingsSection(
|
||||
"NBShaderFeatureLevels",
|
||||
GetProviderLabelContent,
|
||||
OnGUI,
|
||||
new[]
|
||||
{
|
||||
"NBShader",
|
||||
"Feature",
|
||||
"Tier",
|
||||
"Strip",
|
||||
"Keyword",
|
||||
"Quality",
|
||||
"Apply",
|
||||
"Material",
|
||||
"Loaded Materials",
|
||||
"Project Materials",
|
||||
"应用",
|
||||
"材质",
|
||||
"等级",
|
||||
"分级",
|
||||
"剔除"
|
||||
},
|
||||
100);
|
||||
}
|
||||
|
||||
private static void OnGUI(string searchContext)
|
||||
{
|
||||
EnsureContentCacheForCurrentLanguage();
|
||||
|
||||
var settings = NBShaderFeatureLevelProjectSettings.instance;
|
||||
settings.EnsureInitialized();
|
||||
|
||||
var changed = false;
|
||||
|
||||
EditorGUILayout.HelpBox(
|
||||
Text(
|
||||
"featureLevel.help.message",
|
||||
"Configure NBShader managed Catalog keywords and shader passes per tier, and bind Unity Quality levels. Build scripts select the build stripping tier through NBShaderFeatureLevelEditorAPI.OverrideBuildStripExplicitTier. Runtime settings assets can be updated from their own Inspector or Editor API. Catalog-external features are ignored."),
|
||||
MessageType.Info);
|
||||
|
||||
changed |= DrawFeatureLevelTable(settings);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
changed |= DrawButtons(settings);
|
||||
|
||||
if (changed)
|
||||
settings.SaveProjectSettings();
|
||||
}
|
||||
|
||||
private static bool DrawFeatureLevelTable(NBShaderFeatureLevelProjectSettings settings)
|
||||
{
|
||||
var changed = false;
|
||||
|
||||
EditorGUILayout.LabelField(
|
||||
Text("featureLevel.table.title", "Feature Level Matrix"),
|
||||
EditorStyles.boldLabel);
|
||||
|
||||
s_TableScrollPosition = EditorGUILayout.BeginScrollView(
|
||||
s_TableScrollPosition,
|
||||
true,
|
||||
true,
|
||||
GUILayout.MinHeight(TableMinHeight),
|
||||
GUILayout.MaxHeight(TableMaxHeight));
|
||||
|
||||
using (new GUILayout.VerticalScope(GUILayout.Width(TableWidth)))
|
||||
{
|
||||
DrawTableHeader();
|
||||
DrawQualityBindingRow(settings);
|
||||
DrawTableSeparator();
|
||||
changed |= DrawFeatureRows(settings);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
private static void DrawTableHeader()
|
||||
{
|
||||
using (new EditorGUILayout.HorizontalScope(EditorStyles.toolbar, GUILayout.Height(HeaderHeight)))
|
||||
{
|
||||
DrawHeaderCell(
|
||||
Content(
|
||||
"featureLevel.column.feature",
|
||||
"Config / Feature",
|
||||
"Tier settings and managed Catalog features."),
|
||||
FeatureColumnWidth);
|
||||
|
||||
for (var i = 0; i < Tiers.Length; i++)
|
||||
DrawHeaderCell(GetTierContent(Tiers[i]), TierColumnWidth);
|
||||
|
||||
DrawHeaderCell(
|
||||
Content(
|
||||
"featureLevel.column.cost",
|
||||
"Performance Cost",
|
||||
"Estimated shader cost when this feature is enabled."),
|
||||
CostColumnWidth);
|
||||
|
||||
DrawHeaderCell(
|
||||
Content(
|
||||
"featureLevel.column.effect",
|
||||
"Feature Effect",
|
||||
"Short description of what this keyword enables."),
|
||||
EffectColumnWidth);
|
||||
|
||||
DrawHeaderCell(
|
||||
Content(
|
||||
"featureLevel.column.description",
|
||||
"Raw Keyword / Note",
|
||||
"Raw shader keyword or configuration note."),
|
||||
DescriptionColumnWidth);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DrawHeaderCell(GUIContent content, float width)
|
||||
{
|
||||
GUILayout.Label(content, EditorStyles.toolbarButton, GUILayout.Width(width), GUILayout.Height(HeaderHeight));
|
||||
}
|
||||
|
||||
private static void DrawQualityBindingRow(NBShaderFeatureLevelProjectSettings settings)
|
||||
{
|
||||
using (new EditorGUILayout.HorizontalScope(GUILayout.Height(RowHeight)))
|
||||
{
|
||||
GUILayout.Label(
|
||||
Content(
|
||||
"featureLevel.row.quality",
|
||||
"Quality Binding",
|
||||
"Each Unity Quality Level belongs to exactly one NBShader tier."),
|
||||
GUILayout.Width(FeatureColumnWidth),
|
||||
GUILayout.Height(RowHeight));
|
||||
|
||||
for (var i = 0; i < Tiers.Length; i++)
|
||||
{
|
||||
var tier = Tiers[i];
|
||||
if (GUILayout.Button(
|
||||
GetQualitySummaryContent(settings, tier, i),
|
||||
EditorStyles.popup,
|
||||
GUILayout.Width(TierColumnWidth),
|
||||
GUILayout.Height(RowHeight)))
|
||||
{
|
||||
ShowQualityBindingMenu(settings, tier);
|
||||
}
|
||||
}
|
||||
|
||||
DrawCostPlaceholderCell();
|
||||
DrawInfoCell(
|
||||
Content(
|
||||
"featureLevel.effect.quality",
|
||||
"Bind Unity Quality Levels to NBShader tiers.",
|
||||
"Each Unity Quality Level belongs to exactly one NBShader tier."),
|
||||
EffectColumnWidth);
|
||||
DrawInfoCell(
|
||||
GetQualityDescriptionContent(),
|
||||
DescriptionColumnWidth);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool DrawFeatureRows(NBShaderFeatureLevelProjectSettings settings)
|
||||
{
|
||||
var changed = false;
|
||||
var rows = NBShaderFeatureLevelRowCatalog.Rows;
|
||||
var allowedKeywordSets = new HashSet<string>[Tiers.Length];
|
||||
var allowedPassFeatureSets = new HashSet<string>[Tiers.Length];
|
||||
for (var i = 0; i < Tiers.Length; i++)
|
||||
{
|
||||
allowedKeywordSets[i] = settings.GetAllowedKeywordSetForReadOnlyUse(Tiers[i]);
|
||||
allowedPassFeatureSets[i] = settings.GetAllowedPassFeatureSetForReadOnlyUse(Tiers[i]);
|
||||
}
|
||||
|
||||
for (var rowIndex = 0; rowIndex < rows.Length; rowIndex++)
|
||||
{
|
||||
var row = rows[rowIndex];
|
||||
if (!IsRowVisible(row))
|
||||
continue;
|
||||
|
||||
using (var rowScope = new EditorGUILayout.HorizontalScope(GUILayout.Height(RowHeight)))
|
||||
{
|
||||
DrawFeatureRowBackground(rowScope.rect, row);
|
||||
DrawFeatureNameCell(row);
|
||||
|
||||
if (row.isKeyword)
|
||||
{
|
||||
for (var tierIndex = 0; tierIndex < Tiers.Length; tierIndex++)
|
||||
{
|
||||
var tier = Tiers[tierIndex];
|
||||
var isAllowed = allowedKeywordSets[tierIndex].Contains(row.keyword);
|
||||
var newAllowed = DrawCellToggle(isAllowed, GUI.skin.toggle);
|
||||
if (newAllowed == isAllowed)
|
||||
continue;
|
||||
|
||||
Undo.RecordObject(settings, Text("featureLevel.undo.changeKeyword", "Change NBShader Feature Keyword"));
|
||||
settings.SetKeywordAllowed(tier, row.keyword, newAllowed);
|
||||
if (newAllowed)
|
||||
allowedKeywordSets[tierIndex].Add(row.keyword);
|
||||
else
|
||||
allowedKeywordSets[tierIndex].Remove(row.keyword);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
else if (row.isPass)
|
||||
{
|
||||
for (var tierIndex = 0; tierIndex < Tiers.Length; tierIndex++)
|
||||
{
|
||||
var tier = Tiers[tierIndex];
|
||||
var isAllowed = allowedPassFeatureSets[tierIndex].Contains(row.passFeatureId);
|
||||
var newAllowed = DrawCellToggle(isAllowed, GUI.skin.toggle);
|
||||
if (newAllowed == isAllowed)
|
||||
continue;
|
||||
|
||||
Undo.RecordObject(settings, Text("featureLevel.undo.changePassFeature", "Change NBShader Pass Feature"));
|
||||
settings.SetPassFeatureAllowed(tier, row.passFeatureId, newAllowed);
|
||||
if (newAllowed)
|
||||
allowedPassFeatureSets[tierIndex].Add(row.passFeatureId);
|
||||
else
|
||||
allowedPassFeatureSets[tierIndex].Remove(row.passFeatureId);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawEmptyTierCells();
|
||||
}
|
||||
|
||||
DrawCostCell(row);
|
||||
DrawInfoCell(GetEffectContent(row), EffectColumnWidth);
|
||||
DrawInfoCell(
|
||||
GetDescriptionContent(row),
|
||||
DescriptionColumnWidth,
|
||||
row.isKeyword ? EditorStyles.miniLabel : EditorStyles.centeredGreyMiniLabel);
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
private static void DrawFeatureNameCell(NBShaderFeatureLevelRow row)
|
||||
{
|
||||
var rect = EditorGUILayout.GetControlRect(
|
||||
false,
|
||||
RowHeight,
|
||||
GUILayout.Width(FeatureColumnWidth),
|
||||
GUILayout.Height(RowHeight));
|
||||
|
||||
var hasChildren = NBShaderFeatureLevelRowCatalog.HasChildren(row);
|
||||
var indent = row.depth * RowIndentWidth;
|
||||
var foldoutRect = new Rect(rect.x + indent, rect.y, 14f, rect.height);
|
||||
var labelRect = new Rect(
|
||||
rect.x + indent + (hasChildren ? 15f : 18f),
|
||||
rect.y,
|
||||
Mathf.Max(0f, rect.width - indent - 18f),
|
||||
rect.height);
|
||||
|
||||
if (hasChildren)
|
||||
{
|
||||
var expanded = IsExpanded(row.key);
|
||||
var newExpanded = EditorGUI.Foldout(foldoutRect, expanded, GUIContent.none, true);
|
||||
if (newExpanded != expanded)
|
||||
SetExpanded(row.key, newExpanded);
|
||||
}
|
||||
|
||||
var style = hasChildren ? EditorStyles.boldLabel : EditorStyles.label;
|
||||
EditorGUI.LabelField(labelRect, GetRowContent(row), style);
|
||||
}
|
||||
|
||||
private static void DrawFeatureRowBackground(Rect rowRect, NBShaderFeatureLevelRow row)
|
||||
{
|
||||
if (!IsSectionRow(row) || Event.current.type != EventType.Repaint)
|
||||
return;
|
||||
|
||||
rowRect.width = TableWidth;
|
||||
EditorGUI.DrawRect(rowRect, new Color(0f, 0f, 0f, EditorGUIUtility.isProSkin ? 0.22f : 0.08f));
|
||||
}
|
||||
|
||||
private static bool IsSectionRow(NBShaderFeatureLevelRow row)
|
||||
{
|
||||
return row != null &&
|
||||
row.kind == NBShaderFeatureLevelRowKind.Group &&
|
||||
string.IsNullOrEmpty(row.parentKey);
|
||||
}
|
||||
|
||||
private static void DrawEmptyTierCells()
|
||||
{
|
||||
for (var i = 0; i < Tiers.Length; i++)
|
||||
GUILayout.Space(TierColumnWidth);
|
||||
}
|
||||
|
||||
private static void DrawCostCell(NBShaderFeatureLevelRow row)
|
||||
{
|
||||
if (row != null && (row.isKeyword || row.isPass))
|
||||
{
|
||||
DrawInfoCell(GetCostContent(row.performanceCost), CostColumnWidth, EditorStyles.centeredGreyMiniLabel);
|
||||
return;
|
||||
}
|
||||
|
||||
DrawCostPlaceholderCell();
|
||||
}
|
||||
|
||||
private static void DrawCostPlaceholderCell()
|
||||
{
|
||||
DrawInfoCell(GetNoValueContent(), CostColumnWidth, EditorStyles.centeredGreyMiniLabel);
|
||||
}
|
||||
|
||||
private static void DrawInfoCell(GUIContent content, float width, GUIStyle style = null)
|
||||
{
|
||||
GUILayout.Label(
|
||||
content,
|
||||
style ?? EditorStyles.miniLabel,
|
||||
GUILayout.Width(width),
|
||||
GUILayout.Height(RowHeight));
|
||||
}
|
||||
|
||||
private static bool DrawButtons(NBShaderFeatureLevelProjectSettings settings)
|
||||
{
|
||||
var changed = false;
|
||||
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
if (GUILayout.Button(ButtonContent(
|
||||
"featureLevel.resetTierKeywords",
|
||||
"Reset Tier Features",
|
||||
"Reset the keyword and pass matrix to the built-in defaults.")))
|
||||
{
|
||||
Undo.RecordObject(settings, Text("featureLevel.undo.resetTierKeywords", "Reset NBShader Tier Features"));
|
||||
settings.ResetTierKeywordSetsToDefault();
|
||||
settings.ResetTierPassSetsToDefault();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (GUILayout.Button(ButtonContent(
|
||||
"featureLevel.resetQualityMapping",
|
||||
"Reset Quality Mapping",
|
||||
"Reset Unity Quality bindings using the default quality-index rule.")))
|
||||
{
|
||||
Undo.RecordObject(settings, Text("featureLevel.undo.resetQualityMapping", "Reset NBShader Quality Mapping"));
|
||||
settings.ResetQualityMappingsToDefault();
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
if (GUILayout.Button(ButtonContent(
|
||||
"featureLevel.openVariantCollectionBuilder",
|
||||
"Open Variant Collection Builder",
|
||||
"Open the NBShader variant collection builder.")))
|
||||
{
|
||||
NBShaderSVCBuilderWindow.Open();
|
||||
}
|
||||
|
||||
if (GUILayout.Button(ButtonContent(
|
||||
"featureLevel.saveCurrentAsDefault",
|
||||
"Save Current Config as Default",
|
||||
"Write the current tier keyword and pass matrix into the package default LevelAsset.")))
|
||||
{
|
||||
if (!NBShaderFeatureLevelPresetLoader.SaveDefaultFeatureSets(settings.tierKeywordSets, settings.tierPassSets))
|
||||
{
|
||||
EditorUtility.DisplayDialog(
|
||||
Text("featureLevel.saveCurrentAsDefault.failedTitle", "Save Default Config Failed"),
|
||||
Text("featureLevel.saveCurrentAsDefault.failedMessage", "Could not write the package default LevelAsset."),
|
||||
Text("featureLevel.dialog.ok", "OK"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
if (GUILayout.Button(ButtonContent(
|
||||
"featureLevel.applyCurrentQualityTierToLoadedMaterials",
|
||||
"Apply Current Quality Tier To Loaded Materials",
|
||||
"Apply the tier bound to the current Unity Quality Level to all loaded NBShader2 materials.")))
|
||||
{
|
||||
NBShaderEditorQualityTierWatcher.ApplyCurrentQualityTierToLoadedMaterials();
|
||||
}
|
||||
|
||||
if (GUILayout.Button(ButtonContent(
|
||||
"featureLevel.applyCurrentQualityTierToProjectMaterials",
|
||||
"Apply Current Quality Tier To Project Materials",
|
||||
"Scan Assets and apply the tier bound to the current Unity Quality Level to NBShader2 material assets.")))
|
||||
{
|
||||
NBShaderEditorQualityTierWatcher.ApplyCurrentQualityTierToProjectMaterials();
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
private static void ShowQualityBindingMenu(NBShaderFeatureLevelProjectSettings settings, NBShaderFeatureTier targetTier)
|
||||
{
|
||||
var menu = new GenericMenu();
|
||||
var mappings = settings.qualityTierMappings;
|
||||
if (mappings == null || mappings.Length == 0)
|
||||
{
|
||||
menu.AddDisabledItem(new GUIContent(Text("featureLevel.quality.noLevels", "No Quality Levels")));
|
||||
menu.ShowAsContext();
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < mappings.Length; i++)
|
||||
{
|
||||
var mapping = mappings[i];
|
||||
if (mapping == null || string.IsNullOrEmpty(mapping.qualityName))
|
||||
continue;
|
||||
|
||||
var qualityName = mapping.qualityName;
|
||||
NBShaderFeatureTier currentTier;
|
||||
var isCurrentTier = settings.TryGetTierForQualityName(qualityName, out currentTier) && currentTier == targetTier;
|
||||
if (isCurrentTier)
|
||||
{
|
||||
menu.AddDisabledItem(new GUIContent("✓ " + qualityName));
|
||||
continue;
|
||||
}
|
||||
|
||||
menu.AddItem(new GUIContent(qualityName), false, () =>
|
||||
{
|
||||
Undo.RecordObject(settings, Text("featureLevel.undo.changeQualityBinding", "Change NBShader Quality Binding"));
|
||||
settings.MoveQualityToTier(qualityName, targetTier);
|
||||
settings.SaveProjectSettings();
|
||||
UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
|
||||
});
|
||||
}
|
||||
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
|
||||
private static bool DrawCellToggle(bool value, GUIStyle style)
|
||||
{
|
||||
bool result;
|
||||
using (new GUILayout.HorizontalScope(GUILayout.Width(TierColumnWidth), GUILayout.Height(RowHeight)))
|
||||
{
|
||||
GUILayout.FlexibleSpace();
|
||||
result = GUILayout.Toggle(
|
||||
value,
|
||||
GUIContent.none,
|
||||
style,
|
||||
GUILayout.Width(18f),
|
||||
GUILayout.Height(RowHeight));
|
||||
GUILayout.FlexibleSpace();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void DrawTableSeparator()
|
||||
{
|
||||
var rect = EditorGUILayout.GetControlRect(false, 1f, GUILayout.Width(TableWidth));
|
||||
EditorGUI.DrawRect(rect, new Color(0f, 0f, 0f, 0.25f));
|
||||
}
|
||||
|
||||
private static bool IsRowVisible(NBShaderFeatureLevelRow row)
|
||||
{
|
||||
var parentKey = row.parentKey;
|
||||
while (!string.IsNullOrEmpty(parentKey))
|
||||
{
|
||||
if (!IsExpanded(parentKey))
|
||||
return false;
|
||||
|
||||
NBShaderFeatureLevelRow parent;
|
||||
parentKey = NBShaderFeatureLevelRowCatalog.TryGetRow(parentKey, out parent) ? parent.parentKey : null;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsExpanded(string key)
|
||||
{
|
||||
return SessionState.GetBool(FoldoutSessionPrefix + key, true);
|
||||
}
|
||||
|
||||
private static void SetExpanded(string key, bool expanded)
|
||||
{
|
||||
SessionState.SetBool(FoldoutSessionPrefix + key, expanded);
|
||||
}
|
||||
|
||||
private static GUIContent GetTierContent(NBShaderFeatureTier tier)
|
||||
{
|
||||
switch (tier)
|
||||
{
|
||||
case NBShaderFeatureTier.Low:
|
||||
return NBShaderInspectorLocalization.MakeContent(
|
||||
"inspector.toolbar.tierLow.label",
|
||||
"Low",
|
||||
"inspector.toolbar.tier.tip",
|
||||
"NBShader feature tier");
|
||||
case NBShaderFeatureTier.Medium:
|
||||
return NBShaderInspectorLocalization.MakeContent(
|
||||
"inspector.toolbar.tierMedium.label",
|
||||
"Medium",
|
||||
"inspector.toolbar.tier.tip",
|
||||
"NBShader feature tier");
|
||||
case NBShaderFeatureTier.High:
|
||||
return NBShaderInspectorLocalization.MakeContent(
|
||||
"inspector.toolbar.tierHigh.label",
|
||||
"High",
|
||||
"inspector.toolbar.tier.tip",
|
||||
"NBShader feature tier");
|
||||
default:
|
||||
return NBShaderInspectorLocalization.MakeContent(
|
||||
"inspector.toolbar.tierUltra.label",
|
||||
"Ultra",
|
||||
"inspector.toolbar.tier.tip",
|
||||
"NBShader feature tier");
|
||||
}
|
||||
}
|
||||
|
||||
private static GUIContent GetRowContent(NBShaderFeatureLevelRow row)
|
||||
{
|
||||
return GetCachedRowContent(row).rowContent;
|
||||
}
|
||||
|
||||
private static GUIContent BuildRowContent(NBShaderFeatureLevelRow row)
|
||||
{
|
||||
if (row.isKeyword)
|
||||
return BuildKeywordContent(row.keyword, row.labelFallback);
|
||||
|
||||
if (row.isPass)
|
||||
return BuildPassContent(row.passFeatureId, row.labelFallback);
|
||||
|
||||
return NBShaderInspectorLocalization.MakeContent(
|
||||
"inspector.featureLevel.group." + row.key + ".label",
|
||||
row.labelFallback,
|
||||
"inspector.featureLevel.group." + row.key + ".tip",
|
||||
Text("featureLevel.group.tooltip", "Click the foldout to show or hide child rows."));
|
||||
}
|
||||
|
||||
private static GUIContent BuildKeywordContent(string keyword, string fallback)
|
||||
{
|
||||
var label = NBShaderInspectorLocalization.Get(
|
||||
"inspector.featureLevel.keyword." + keyword + ".label",
|
||||
string.IsNullOrEmpty(fallback) ? keyword : fallback);
|
||||
var tooltipFormat = Text(
|
||||
"featureLevel.keyword.tooltip",
|
||||
"Raw keyword: {0}. Unchecked in a tier strips variants using this Catalog keyword.");
|
||||
return new GUIContent(label, string.Format(tooltipFormat, keyword));
|
||||
}
|
||||
|
||||
private static GUIContent BuildPassContent(string passFeatureId, string fallback)
|
||||
{
|
||||
string passName;
|
||||
string displayName;
|
||||
if (!NBShaderFeatureLevelCatalog.TryGetManagedPassFeatureInfo(passFeatureId, out passName, out displayName))
|
||||
{
|
||||
passName = passFeatureId;
|
||||
displayName = string.IsNullOrEmpty(fallback) ? passFeatureId : fallback;
|
||||
}
|
||||
|
||||
var label = NBShaderInspectorLocalization.Get(
|
||||
"inspector.featureLevel.pass." + passFeatureId + ".label",
|
||||
string.IsNullOrEmpty(fallback) ? displayName : fallback);
|
||||
var tooltipFormat = Text(
|
||||
"featureLevel.pass.tooltip",
|
||||
"Shader pass: {0}. Unchecked in a tier strips or gates this pass when material intent requires it.");
|
||||
return new GUIContent(label, string.Format(tooltipFormat, passName));
|
||||
}
|
||||
|
||||
private static GUIContent GetCostContent(NBShaderFeaturePerformanceCost cost)
|
||||
{
|
||||
GUIContent content;
|
||||
if (s_CostContentCache.TryGetValue(cost, out content))
|
||||
return content;
|
||||
|
||||
string key;
|
||||
string fallback;
|
||||
switch (cost)
|
||||
{
|
||||
case NBShaderFeaturePerformanceCost.Low:
|
||||
key = "featureLevel.cost.low";
|
||||
fallback = "Low";
|
||||
break;
|
||||
case NBShaderFeaturePerformanceCost.Medium:
|
||||
key = "featureLevel.cost.medium";
|
||||
fallback = "Medium";
|
||||
break;
|
||||
case NBShaderFeaturePerformanceCost.High:
|
||||
key = "featureLevel.cost.high";
|
||||
fallback = "High";
|
||||
break;
|
||||
default:
|
||||
key = "featureLevel.cost.ultra";
|
||||
fallback = "Ultra";
|
||||
break;
|
||||
}
|
||||
|
||||
content = new GUIContent(
|
||||
Text(key, fallback),
|
||||
Text("featureLevel.cost.tooltip", "Estimated shader performance cost for this feature."));
|
||||
s_CostContentCache[cost] = content;
|
||||
return content;
|
||||
}
|
||||
|
||||
private static GUIContent GetEffectContent(NBShaderFeatureLevelRow row)
|
||||
{
|
||||
return GetCachedRowContent(row).effectContent;
|
||||
}
|
||||
|
||||
private static GUIContent BuildEffectContent(NBShaderFeatureLevelRow row)
|
||||
{
|
||||
if (row.isKeyword)
|
||||
{
|
||||
var effect = NBShaderInspectorLocalization.Get(
|
||||
"inspector.featureLevel.keyword." + row.keyword + ".effect",
|
||||
string.IsNullOrEmpty(row.effectFallback) ? row.keyword : row.effectFallback);
|
||||
return new GUIContent(effect, effect);
|
||||
}
|
||||
|
||||
if (row.isPass)
|
||||
{
|
||||
var effect = NBShaderInspectorLocalization.Get(
|
||||
"inspector.featureLevel.pass." + row.passFeatureId + ".effect",
|
||||
string.IsNullOrEmpty(row.effectFallback) ? row.passFeatureId : row.effectFallback);
|
||||
return new GUIContent(effect, effect);
|
||||
}
|
||||
|
||||
var groupTip = NBShaderInspectorLocalization.GetTooltip(
|
||||
"inspector.featureLevel.group." + row.key + ".label",
|
||||
"inspector.featureLevel.group." + row.key + ".tip",
|
||||
Text("featureLevel.group.tooltip", "Click the foldout to show or hide child rows."));
|
||||
return new GUIContent(groupTip, groupTip);
|
||||
}
|
||||
|
||||
private static GUIContent GetDescriptionContent(NBShaderFeatureLevelRow row)
|
||||
{
|
||||
return GetCachedRowContent(row).descriptionContent;
|
||||
}
|
||||
|
||||
private static GUIContent BuildDescriptionContent(NBShaderFeatureLevelRow row)
|
||||
{
|
||||
if (row.isKeyword)
|
||||
return new GUIContent(row.keyword, row.keyword);
|
||||
|
||||
if (row.isPass)
|
||||
{
|
||||
string passName;
|
||||
string displayName;
|
||||
if (NBShaderFeatureLevelCatalog.TryGetManagedPassFeatureInfo(row.passFeatureId, out passName, out displayName))
|
||||
return new GUIContent(passName, row.passFeatureId);
|
||||
|
||||
return new GUIContent(row.passFeatureId, row.passFeatureId);
|
||||
}
|
||||
|
||||
return new GUIContent(
|
||||
Text("featureLevel.desc.group", "Group"),
|
||||
Text("featureLevel.group.tooltip", "Click the foldout to show or hide child rows."));
|
||||
}
|
||||
|
||||
private static GUIContent GetNoValueContent()
|
||||
{
|
||||
if (s_NoValueContent != null)
|
||||
return s_NoValueContent;
|
||||
|
||||
var text = Text("featureLevel.desc.none", "—");
|
||||
s_NoValueContent = new GUIContent(text, text);
|
||||
return s_NoValueContent;
|
||||
}
|
||||
|
||||
private static GUIContent GetProviderLabelContent()
|
||||
{
|
||||
EnsureContentCacheForCurrentLanguage();
|
||||
|
||||
if (s_ProviderLabelContent == null)
|
||||
s_ProviderLabelContent = new GUIContent(Text("featureLevel.providerLabel", "NBShader Feature Levels"));
|
||||
|
||||
return s_ProviderLabelContent;
|
||||
}
|
||||
|
||||
private static GUIContent GetQualityDescriptionContent()
|
||||
{
|
||||
if (s_QualityDescriptionContent == null)
|
||||
{
|
||||
var text = Text("featureLevel.desc.quality", "Unity Quality Level");
|
||||
s_QualityDescriptionContent = new GUIContent(text, text);
|
||||
}
|
||||
|
||||
return s_QualityDescriptionContent;
|
||||
}
|
||||
|
||||
private static GUIContent GetQualitySummaryContent(
|
||||
NBShaderFeatureLevelProjectSettings settings,
|
||||
NBShaderFeatureTier tier,
|
||||
int tierIndex)
|
||||
{
|
||||
EnsureQualitySummaryCache(settings);
|
||||
|
||||
var content = s_QualitySummaryContents[tierIndex];
|
||||
if (content == null)
|
||||
{
|
||||
content = new GUIContent();
|
||||
s_QualitySummaryContents[tierIndex] = content;
|
||||
}
|
||||
|
||||
content.text = s_QualitySummaryTexts[tierIndex];
|
||||
content.tooltip = GetQualityMenuTipText();
|
||||
return content;
|
||||
}
|
||||
|
||||
private static void EnsureQualitySummaryCache(NBShaderFeatureLevelProjectSettings settings)
|
||||
{
|
||||
var mappings = settings.qualityTierMappings;
|
||||
var hash = GetQualitySummaryHash(mappings);
|
||||
if (s_QualitySummaryTexts[0] != null && s_QualitySummaryHash == hash)
|
||||
return;
|
||||
|
||||
s_QualitySummaryHash = hash;
|
||||
var noneText = Text("featureLevel.quality.none", "—");
|
||||
for (var tierIndex = 0; tierIndex < Tiers.Length; tierIndex++)
|
||||
{
|
||||
s_QualitySummaryBuilder.Length = 0;
|
||||
if (mappings != null)
|
||||
{
|
||||
var tier = Tiers[tierIndex];
|
||||
for (var i = 0; i < mappings.Length; i++)
|
||||
{
|
||||
var mapping = mappings[i];
|
||||
if (mapping == null ||
|
||||
mapping.tier != tier ||
|
||||
string.IsNullOrEmpty(mapping.qualityName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (s_QualitySummaryBuilder.Length > 0)
|
||||
s_QualitySummaryBuilder.Append(", ");
|
||||
s_QualitySummaryBuilder.Append(mapping.qualityName);
|
||||
}
|
||||
}
|
||||
|
||||
s_QualitySummaryTexts[tierIndex] =
|
||||
s_QualitySummaryBuilder.Length == 0
|
||||
? noneText
|
||||
: s_QualitySummaryBuilder.ToString();
|
||||
}
|
||||
|
||||
s_QualitySummaryBuilder.Length = 0;
|
||||
}
|
||||
|
||||
private static int GetQualitySummaryHash(NBShaderQualityTierMapping[] mappings)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
var hash = 17;
|
||||
if (mappings == null)
|
||||
return hash;
|
||||
|
||||
for (var i = 0; i < mappings.Length; i++)
|
||||
{
|
||||
var mapping = mappings[i];
|
||||
hash = hash * 31 + (mapping != null ? (int)mapping.tier : -1);
|
||||
hash = hash * 31 + (mapping != null && mapping.qualityName != null
|
||||
? StringComparer.Ordinal.GetHashCode(mapping.qualityName)
|
||||
: 0);
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetQualityMenuTipText()
|
||||
{
|
||||
if (s_QualityMenuTipText == null)
|
||||
{
|
||||
s_QualityMenuTipText = Text(
|
||||
"featureLevel.quality.menuTip",
|
||||
"Click to move Unity Quality levels to this NBShader tier.");
|
||||
}
|
||||
|
||||
return s_QualityMenuTipText;
|
||||
}
|
||||
|
||||
private static RowContentCache GetCachedRowContent(NBShaderFeatureLevelRow row)
|
||||
{
|
||||
RowContentCache cache;
|
||||
if (!s_RowContentCache.TryGetValue(row.key, out cache))
|
||||
{
|
||||
cache = new RowContentCache
|
||||
{
|
||||
rowContent = BuildRowContent(row),
|
||||
effectContent = BuildEffectContent(row),
|
||||
descriptionContent = BuildDescriptionContent(row)
|
||||
};
|
||||
s_RowContentCache.Add(row.key, cache);
|
||||
}
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
private static void EnsureContentCacheForCurrentLanguage()
|
||||
{
|
||||
var language = NBShaderInspectorLocalization.CurrentLanguage;
|
||||
if (string.Equals(s_ContentCacheLanguage, language, StringComparison.Ordinal))
|
||||
return;
|
||||
|
||||
s_ContentCacheLanguage = language;
|
||||
s_RowContentCache.Clear();
|
||||
s_CostContentCache.Clear();
|
||||
s_ProviderLabelContent = null;
|
||||
s_QualityDescriptionContent = null;
|
||||
s_NoValueContent = null;
|
||||
s_QualityMenuTipText = null;
|
||||
s_QualitySummaryHash = 0;
|
||||
for (var i = 0; i < s_QualitySummaryContents.Length; i++)
|
||||
{
|
||||
s_QualitySummaryContents[i] = null;
|
||||
s_QualitySummaryTexts[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static GUIContent Content(string key, string fallback, string tip = "")
|
||||
{
|
||||
return NBShaderInspectorLocalization.MakeInspectorContent(key, fallback, tip);
|
||||
}
|
||||
|
||||
private static GUIContent ButtonContent(string key, string fallback, string tip = "")
|
||||
{
|
||||
return NBShaderInspectorLocalization.MakeContent("inspector." + key + ".button", fallback, null, tip);
|
||||
}
|
||||
|
||||
private static string Text(string key, string fallback)
|
||||
{
|
||||
return NBShaderInspectorLocalization.GetInspectorText(key, fallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 278a2a702fc5d47e3a7e5c61468cc2c7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using NBShader;
|
||||
|
||||
namespace NBShaders2.Editor.FeatureLevel
|
||||
{
|
||||
[Serializable]
|
||||
public sealed class NBShaderFeatureTierKeywordSet
|
||||
{
|
||||
public NBShaderFeatureTier tier;
|
||||
public string[] allowedKeywords = new string[0];
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public sealed class NBShaderFeatureTierPassSet
|
||||
{
|
||||
public NBShaderFeatureTier tier;
|
||||
public string[] allowedPassFeatures = new string[0];
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public sealed class NBShaderQualityTierMapping
|
||||
{
|
||||
public string qualityName;
|
||||
public NBShaderFeatureTier tier;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 24ec2067d0e81465ebeab015048983e0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,58 @@
|
||||
using NBShader;
|
||||
using NBShaderEditor;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBShaders2.Editor.FeatureLevel
|
||||
{
|
||||
[CustomEditor(typeof(NBShaderFeatureRuntimeSettings))]
|
||||
public sealed class NBShaderFeatureRuntimeSettingsEditor : UnityEditor.Editor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
DrawSyncButton();
|
||||
|
||||
EditorGUILayout.Space();
|
||||
DrawDefaultInspector();
|
||||
}
|
||||
|
||||
private void DrawSyncButton()
|
||||
{
|
||||
var settings = (NBShaderFeatureRuntimeSettings)target;
|
||||
using (new EditorGUI.DisabledScope(settings == null))
|
||||
{
|
||||
if (!GUILayout.Button(ButtonContent(
|
||||
"featureRuntimeSettings.updateFromProjectSettings",
|
||||
"根据当前ProjectSetting更新配置",
|
||||
"Write the current NBShader Project Settings data into this runtime settings asset.")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Undo.RecordObject(
|
||||
settings,
|
||||
Text(
|
||||
"featureRuntimeSettings.updateFromProjectSettings.undo",
|
||||
"Update NBShader Runtime Settings From Project Settings"));
|
||||
|
||||
if (NBShaderRuntimeSettingsSynchronizer.WriteProjectSettingsToRuntimeAsset(settings))
|
||||
{
|
||||
EditorUtility.DisplayDialog(
|
||||
Text("featureRuntimeSettings.updateFromProjectSettings.successTitle", "Runtime Settings Updated"),
|
||||
Text("featureRuntimeSettings.updateFromProjectSettings.successMessage", "The current NBShader Project Settings data was written to this runtime settings asset."),
|
||||
Text("featureLevel.dialog.ok", "OK"));
|
||||
}
|
||||
}
|
||||
|
||||
private static GUIContent ButtonContent(string key, string fallback, string tip)
|
||||
{
|
||||
return NBShaderInspectorLocalization.MakeInspectorContent(key + ".button", fallback, tip);
|
||||
}
|
||||
|
||||
private static string Text(string key, string fallback)
|
||||
{
|
||||
return NBShaderInspectorLocalization.GetInspectorText(key, fallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 86834a41402844679aead46b69ad7fdf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,241 @@
|
||||
using System.Collections.Generic;
|
||||
using NBShader;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBShaders2.Editor.FeatureLevel
|
||||
{
|
||||
public static class NBShaderRuntimeSettingsSynchronizer
|
||||
{
|
||||
public const string DefaultGeneratedRuntimeSettingsAssetPath =
|
||||
"Assets/NBShaderGenerated/RuntimeSettings/NBShaderFeatureRuntimeSettings.asset";
|
||||
|
||||
private static bool s_DefaultGeneratedRuntimeSettingsWarningLogged;
|
||||
|
||||
public static bool WriteProjectSettingsToRuntimeAsset(NBShaderFeatureRuntimeSettings asset)
|
||||
{
|
||||
if (asset == null)
|
||||
{
|
||||
Debug.LogWarning("NBShader runtime settings asset is null. Pass an explicit NBShaderFeatureRuntimeSettings asset before writing.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var settings = NBShaderFeatureLevelProjectSettings.instance;
|
||||
settings.EnsureInitialized();
|
||||
ApplyProjectSettingsToRuntimeObject(asset, settings);
|
||||
EditorUtility.SetDirty(asset);
|
||||
AssetDatabase.SaveAssetIfDirty(asset);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool WriteProjectSettingsSnapshotToRuntimeAssetNoSave(NBShaderFeatureRuntimeSettings asset)
|
||||
{
|
||||
if (asset == null)
|
||||
{
|
||||
Debug.LogWarning("NBShader runtime settings asset is null. Pass an explicit NBShaderFeatureRuntimeSettings asset before writing.");
|
||||
return false;
|
||||
}
|
||||
|
||||
ApplyProjectSettingsSnapshotToRuntimeObjectNoSave(asset, NBShaderFeatureLevelProjectSettings.instance);
|
||||
EditorUtility.SetDirty(asset);
|
||||
AssetDatabase.SaveAssetIfDirty(asset);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static NBShaderFeatureRuntimeSettings WriteDefaultGeneratedRuntimeSettingsAsset(out string assetPath)
|
||||
{
|
||||
assetPath = DefaultGeneratedRuntimeSettingsAssetPath;
|
||||
LogDefaultGeneratedRuntimeSettingsWarningOnce(assetPath);
|
||||
|
||||
var asset = LoadOrCreateRuntimeSettingsAsset(assetPath);
|
||||
if (asset == null)
|
||||
return null;
|
||||
|
||||
return WriteProjectSettingsToRuntimeAsset(asset) ? asset : null;
|
||||
}
|
||||
|
||||
public static NBShaderFeatureRuntimeSettings WriteRuntimeSettingsAssetOrDefault(
|
||||
NBShaderFeatureRuntimeSettings explicitAsset,
|
||||
out string assetPath)
|
||||
{
|
||||
if (explicitAsset == null)
|
||||
return WriteDefaultGeneratedRuntimeSettingsAsset(out assetPath);
|
||||
|
||||
assetPath = AssetDatabase.GetAssetPath(explicitAsset);
|
||||
return WriteProjectSettingsToRuntimeAsset(explicitAsset) ? explicitAsset : null;
|
||||
}
|
||||
|
||||
private static void ApplyProjectSettingsToRuntimeObject(
|
||||
NBShaderFeatureRuntimeSettings asset,
|
||||
NBShaderFeatureLevelProjectSettings settings)
|
||||
{
|
||||
asset.lowAllowedKeywords = ToCatalogOrderedKeywords(settings.GetAllowedKeywordSet(NBShaderFeatureTier.Low));
|
||||
asset.mediumAllowedKeywords = ToCatalogOrderedKeywords(settings.GetAllowedKeywordSet(NBShaderFeatureTier.Medium));
|
||||
asset.highAllowedKeywords = ToCatalogOrderedKeywords(settings.GetAllowedKeywordSet(NBShaderFeatureTier.High));
|
||||
asset.ultraAllowedKeywords = ToCatalogOrderedKeywords(settings.GetAllowedKeywordSet(NBShaderFeatureTier.Ultra));
|
||||
|
||||
asset.lowAllowedPassFeatures = ToCatalogOrderedPassFeatures(settings.GetAllowedPassFeatureSet(NBShaderFeatureTier.Low));
|
||||
asset.mediumAllowedPassFeatures = ToCatalogOrderedPassFeatures(settings.GetAllowedPassFeatureSet(NBShaderFeatureTier.Medium));
|
||||
asset.highAllowedPassFeatures = ToCatalogOrderedPassFeatures(settings.GetAllowedPassFeatureSet(NBShaderFeatureTier.High));
|
||||
asset.ultraAllowedPassFeatures = ToCatalogOrderedPassFeatures(settings.GetAllowedPassFeatureSet(NBShaderFeatureTier.Ultra));
|
||||
|
||||
asset.qualityTierMappings = ConvertQualityMappings(settings.qualityTierMappings);
|
||||
}
|
||||
|
||||
private static void ApplyProjectSettingsSnapshotToRuntimeObjectNoSave(
|
||||
NBShaderFeatureRuntimeSettings asset,
|
||||
NBShaderFeatureLevelProjectSettings settings)
|
||||
{
|
||||
asset.lowAllowedKeywords = ToCatalogOrderedKeywords(settings.GetAllowedKeywordSetForBuildInfoNoSave(NBShaderFeatureTier.Low));
|
||||
asset.mediumAllowedKeywords = ToCatalogOrderedKeywords(settings.GetAllowedKeywordSetForBuildInfoNoSave(NBShaderFeatureTier.Medium));
|
||||
asset.highAllowedKeywords = ToCatalogOrderedKeywords(settings.GetAllowedKeywordSetForBuildInfoNoSave(NBShaderFeatureTier.High));
|
||||
asset.ultraAllowedKeywords = ToCatalogOrderedKeywords(settings.GetAllowedKeywordSetForBuildInfoNoSave(NBShaderFeatureTier.Ultra));
|
||||
|
||||
asset.lowAllowedPassFeatures = ToCatalogOrderedPassFeatures(settings.GetAllowedPassFeatureSetForBuildInfoNoSave(NBShaderFeatureTier.Low));
|
||||
asset.mediumAllowedPassFeatures = ToCatalogOrderedPassFeatures(settings.GetAllowedPassFeatureSetForBuildInfoNoSave(NBShaderFeatureTier.Medium));
|
||||
asset.highAllowedPassFeatures = ToCatalogOrderedPassFeatures(settings.GetAllowedPassFeatureSetForBuildInfoNoSave(NBShaderFeatureTier.High));
|
||||
asset.ultraAllowedPassFeatures = ToCatalogOrderedPassFeatures(settings.GetAllowedPassFeatureSetForBuildInfoNoSave(NBShaderFeatureTier.Ultra));
|
||||
|
||||
asset.qualityTierMappings = ConvertQualityMappingsNoSave(settings);
|
||||
}
|
||||
|
||||
private static NBShaderFeatureRuntimeSettings.QualityTierMapping[] ConvertQualityMappings(NBShaderQualityTierMapping[] source)
|
||||
{
|
||||
if (source == null || source.Length == 0)
|
||||
return new NBShaderFeatureRuntimeSettings.QualityTierMapping[0];
|
||||
|
||||
var result = new NBShaderFeatureRuntimeSettings.QualityTierMapping[source.Length];
|
||||
for (var i = 0; i < source.Length; i++)
|
||||
{
|
||||
var item = source[i];
|
||||
result[i] = new NBShaderFeatureRuntimeSettings.QualityTierMapping
|
||||
{
|
||||
qualityName = item != null ? item.qualityName : string.Empty,
|
||||
tier = item != null ? item.tier : NBShaderFeatureTier.Ultra
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static NBShaderFeatureRuntimeSettings.QualityTierMapping[] ConvertQualityMappingsNoSave(NBShaderFeatureLevelProjectSettings settings)
|
||||
{
|
||||
var qualityNames = QualitySettings.names;
|
||||
if (qualityNames == null || qualityNames.Length == 0)
|
||||
qualityNames = new[] { "Default" };
|
||||
|
||||
var result = new NBShaderFeatureRuntimeSettings.QualityTierMapping[qualityNames.Length];
|
||||
for (var i = 0; i < qualityNames.Length; i++)
|
||||
{
|
||||
var qualityName = qualityNames[i];
|
||||
NBShaderFeatureTier tier;
|
||||
if (!settings.TryGetTierForQualityNameNoSave(qualityName, out tier))
|
||||
tier = NBShaderFeatureTier.Ultra;
|
||||
|
||||
result[i] = new NBShaderFeatureRuntimeSettings.QualityTierMapping
|
||||
{
|
||||
qualityName = qualityName,
|
||||
tier = tier
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static string[] ToCatalogOrderedKeywords(HashSet<string> allowed)
|
||||
{
|
||||
return ToCatalogOrderedArray(allowed, NBShaderFeatureCatalog.RawKeywords);
|
||||
}
|
||||
|
||||
private static string[] ToCatalogOrderedPassFeatures(HashSet<string> allowed)
|
||||
{
|
||||
return ToCatalogOrderedArray(allowed, NBShaderPassFeatureCatalog.RawPassFeatureIds);
|
||||
}
|
||||
|
||||
private static string[] ToCatalogOrderedArray(HashSet<string> allowed, string[] catalogOrder)
|
||||
{
|
||||
if (allowed == null || catalogOrder == null)
|
||||
return new string[0];
|
||||
|
||||
var result = new List<string>();
|
||||
for (var i = 0; i < catalogOrder.Length; i++)
|
||||
{
|
||||
var value = catalogOrder[i];
|
||||
if (allowed.Contains(value))
|
||||
result.Add(value);
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
private static NBShaderFeatureRuntimeSettings LoadOrCreateRuntimeSettingsAsset(string assetPath)
|
||||
{
|
||||
var existing = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(assetPath);
|
||||
if (existing == null)
|
||||
{
|
||||
if (!EnsureAssetFolder(assetPath))
|
||||
return null;
|
||||
|
||||
var asset = ScriptableObject.CreateInstance<NBShaderFeatureRuntimeSettings>();
|
||||
AssetDatabase.CreateAsset(asset, assetPath);
|
||||
return asset;
|
||||
}
|
||||
|
||||
var runtimeSettings = existing as NBShaderFeatureRuntimeSettings;
|
||||
if (runtimeSettings != null)
|
||||
return runtimeSettings;
|
||||
|
||||
Debug.LogError("NBShader default runtime settings output path is occupied by another asset type: " + assetPath);
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool EnsureAssetFolder(string assetPath)
|
||||
{
|
||||
var slashIndex = assetPath.LastIndexOf('/');
|
||||
if (slashIndex <= 0)
|
||||
return true;
|
||||
|
||||
var folderPath = assetPath.Substring(0, slashIndex);
|
||||
var parts = folderPath.Split('/');
|
||||
if (parts.Length == 0 || parts[0] != "Assets")
|
||||
{
|
||||
Debug.LogError("NBShader default runtime settings asset path must be under Assets: " + assetPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
var current = "Assets";
|
||||
for (var i = 1; i < parts.Length; i++)
|
||||
{
|
||||
var part = parts[i];
|
||||
if (string.IsNullOrEmpty(part))
|
||||
continue;
|
||||
|
||||
var next = current + "/" + part;
|
||||
if (!AssetDatabase.IsValidFolder(next))
|
||||
AssetDatabase.CreateFolder(current, part);
|
||||
|
||||
if (!AssetDatabase.IsValidFolder(next))
|
||||
{
|
||||
Debug.LogError("Failed to create NBShader runtime settings folder: " + next);
|
||||
return false;
|
||||
}
|
||||
|
||||
current = next;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void LogDefaultGeneratedRuntimeSettingsWarningOnce(string assetPath)
|
||||
{
|
||||
if (s_DefaultGeneratedRuntimeSettingsWarningLogged)
|
||||
return;
|
||||
|
||||
s_DefaultGeneratedRuntimeSettingsWarningLogged = true;
|
||||
Debug.LogWarning(
|
||||
"NBShader runtime settings asset was not explicitly specified. Generated one from current Project Settings at " +
|
||||
assetPath +
|
||||
". Include this asset in the player, Addressables, Resources, or shader AssetBundle before runtime code passes it to NBShaderFeatureRuntime.ApplyTier.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 24198ba1f30a4441782ea031021cfbfb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,290 @@
|
||||
using System.Collections.Generic;
|
||||
using NBShader;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBShaders2.Editor.FeatureLevel
|
||||
{
|
||||
internal sealed class NBShaderSVCBuilderWindow : EditorWindow
|
||||
{
|
||||
[SerializeField] private List<string> m_SearchFolders = new List<string>();
|
||||
[SerializeField] private string m_OutputFolder = NBShaderVariantCollectionBuilder.DefaultOutputFolder;
|
||||
[SerializeField] private int m_TierMask = AllTierMask;
|
||||
|
||||
private Vector2 m_Scroll;
|
||||
private NBShaderVariantCollectionTierResult[] m_Previews = new NBShaderVariantCollectionTierResult[0];
|
||||
private string m_Status = string.Empty;
|
||||
|
||||
private const int AllTierMask =
|
||||
(1 << (int)NBShaderFeatureTier.Low) |
|
||||
(1 << (int)NBShaderFeatureTier.Medium) |
|
||||
(1 << (int)NBShaderFeatureTier.High) |
|
||||
(1 << (int)NBShaderFeatureTier.Ultra);
|
||||
|
||||
private static readonly NBShaderFeatureTier[] Tiers =
|
||||
{
|
||||
NBShaderFeatureTier.Low,
|
||||
NBShaderFeatureTier.Medium,
|
||||
NBShaderFeatureTier.High,
|
||||
NBShaderFeatureTier.Ultra
|
||||
};
|
||||
|
||||
internal static void Open()
|
||||
{
|
||||
var window = GetWindow<NBShaderSVCBuilderWindow>("NBShader Variant Collection Builder");
|
||||
window.minSize = new Vector2(620f, 420f);
|
||||
window.Show();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (m_SearchFolders == null)
|
||||
m_SearchFolders = new List<string>();
|
||||
if (m_SearchFolders.Count == 0)
|
||||
m_SearchFolders.Add("Assets");
|
||||
if (string.IsNullOrEmpty(m_OutputFolder))
|
||||
m_OutputFolder = NBShaderVariantCollectionBuilder.DefaultOutputFolder;
|
||||
if (m_TierMask == 0)
|
||||
m_TierMask = AllTierMask;
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
m_Scroll = EditorGUILayout.BeginScrollView(m_Scroll);
|
||||
DrawSearchFolders();
|
||||
EditorGUILayout.Space(8f);
|
||||
DrawOutputFolder();
|
||||
EditorGUILayout.Space(8f);
|
||||
DrawTierSelection();
|
||||
EditorGUILayout.Space(8f);
|
||||
DrawActions();
|
||||
EditorGUILayout.Space(8f);
|
||||
DrawPreview();
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
private void DrawSearchFolders()
|
||||
{
|
||||
EditorGUILayout.LabelField("Material Search Folders", EditorStyles.boldLabel);
|
||||
for (var i = 0; i < m_SearchFolders.Count; i++)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
m_SearchFolders[i] = EditorGUILayout.TextField(m_SearchFolders[i]);
|
||||
if (GUILayout.Button("...", GUILayout.Width(28f)))
|
||||
{
|
||||
var folder = PickProjectFolder(m_SearchFolders[i]);
|
||||
if (!string.IsNullOrEmpty(folder))
|
||||
m_SearchFolders[i] = folder;
|
||||
}
|
||||
|
||||
if (GUILayout.Button("-", GUILayout.Width(28f)))
|
||||
{
|
||||
m_SearchFolders.RemoveAt(i);
|
||||
GUIUtility.ExitGUI();
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("Add Folder"))
|
||||
{
|
||||
var folder = PickProjectFolder("Assets");
|
||||
if (!string.IsNullOrEmpty(folder))
|
||||
AddSearchFolder(folder);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Add Selected"))
|
||||
{
|
||||
AddSelectedFolders();
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private void DrawOutputFolder()
|
||||
{
|
||||
EditorGUILayout.LabelField("Output Folder", EditorStyles.boldLabel);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
m_OutputFolder = EditorGUILayout.TextField(m_OutputFolder);
|
||||
if (GUILayout.Button("...", GUILayout.Width(28f)))
|
||||
{
|
||||
var folder = PickProjectFolder(m_OutputFolder);
|
||||
if (!string.IsNullOrEmpty(folder))
|
||||
m_OutputFolder = folder;
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private void DrawTierSelection()
|
||||
{
|
||||
EditorGUILayout.LabelField("Build Tiers", EditorStyles.boldLabel);
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
for (var i = 0; i < Tiers.Length; i++)
|
||||
DrawTierToggle(Tiers[i]);
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
if (GUILayout.Button("All", GUILayout.Width(56f)))
|
||||
SetTierMask(AllTierMask);
|
||||
}
|
||||
|
||||
if (!HasSelectedTier())
|
||||
EditorGUILayout.HelpBox("Select at least one tier before previewing or generating SVC assets.", MessageType.Warning);
|
||||
}
|
||||
|
||||
private void DrawTierToggle(NBShaderFeatureTier tier)
|
||||
{
|
||||
var selected = IsTierSelected(tier);
|
||||
var newSelected = EditorGUILayout.ToggleLeft(tier.ToString(), selected, GUILayout.Width(92f));
|
||||
if (newSelected == selected)
|
||||
return;
|
||||
|
||||
var mask = 1 << (int)tier;
|
||||
if (newSelected)
|
||||
SetTierMask(m_TierMask | mask);
|
||||
else
|
||||
SetTierMask(m_TierMask & ~mask);
|
||||
}
|
||||
|
||||
private void DrawActions()
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
using (new EditorGUI.DisabledScope(!HasSelectedTier()))
|
||||
{
|
||||
if (GUILayout.Button("Preview", GUILayout.Height(28f)))
|
||||
Preview();
|
||||
}
|
||||
|
||||
using (new EditorGUI.DisabledScope(!HasSelectedTier() || m_Previews == null || m_Previews.Length == 0))
|
||||
{
|
||||
if (GUILayout.Button("Generate SVC", GUILayout.Height(28f)))
|
||||
Generate();
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
if (!string.IsNullOrEmpty(m_Status))
|
||||
EditorGUILayout.HelpBox(m_Status, MessageType.Info);
|
||||
}
|
||||
|
||||
private void DrawPreview()
|
||||
{
|
||||
if (m_Previews == null || m_Previews.Length == 0)
|
||||
return;
|
||||
|
||||
EditorGUILayout.LabelField("Preview", EditorStyles.boldLabel);
|
||||
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
|
||||
{
|
||||
DrawPreviewHeader();
|
||||
for (var i = 0; i < m_Previews.Length; i++)
|
||||
{
|
||||
var preview = m_Previews[i];
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField(preview.tier.ToString(), GUILayout.Width(90f));
|
||||
EditorGUILayout.LabelField(preview.materialCount.ToString(), GUILayout.Width(90f));
|
||||
EditorGUILayout.LabelField(preview.passCount.ToString(), GUILayout.Width(70f));
|
||||
EditorGUILayout.LabelField(preview.variantCount.ToString(), GUILayout.Width(90f));
|
||||
EditorGUILayout.LabelField(preview.outputAssetPath ?? string.Empty);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void DrawPreviewHeader()
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
||||
EditorGUILayout.LabelField("Tier", EditorStyles.boldLabel, GUILayout.Width(90f));
|
||||
EditorGUILayout.LabelField("Materials", EditorStyles.boldLabel, GUILayout.Width(90f));
|
||||
EditorGUILayout.LabelField("Passes", EditorStyles.boldLabel, GUILayout.Width(70f));
|
||||
EditorGUILayout.LabelField("Variants", EditorStyles.boldLabel, GUILayout.Width(90f));
|
||||
EditorGUILayout.LabelField("Output", EditorStyles.boldLabel);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private void Preview()
|
||||
{
|
||||
var selectedTiers = GetSelectedTiers();
|
||||
var result = NBShaderVariantCollectionBuilder.Preview(m_SearchFolders, m_OutputFolder, selectedTiers);
|
||||
m_Previews = result.tiers;
|
||||
m_Status = string.Format(
|
||||
"Found {0} NBShader materials in {1} valid folder(s). Previewed {2} tier(s) and did not write assets.",
|
||||
result.materialCount,
|
||||
result.validSearchFolderCount,
|
||||
selectedTiers.Length);
|
||||
}
|
||||
|
||||
private void Generate()
|
||||
{
|
||||
var result = NBShaderVariantCollectionBuilder.Generate(m_SearchFolders, m_OutputFolder, GetSelectedTiers());
|
||||
if (result.hasError)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Generate NBShader SVC", result.firstErrorMessage, "OK");
|
||||
m_Previews = result.tiers;
|
||||
return;
|
||||
}
|
||||
|
||||
m_Previews = result.tiers;
|
||||
m_Status = string.Format("Generated {0} NBShader shader variant collection asset(s).", result.generatedCount);
|
||||
}
|
||||
|
||||
private void AddSelectedFolders()
|
||||
{
|
||||
var guids = Selection.assetGUIDs;
|
||||
for (var i = 0; i < guids.Length; i++)
|
||||
{
|
||||
var path = AssetDatabase.GUIDToAssetPath(guids[i]);
|
||||
if (AssetDatabase.IsValidFolder(path))
|
||||
AddSearchFolder(path);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddSearchFolder(string folder)
|
||||
{
|
||||
folder = NBShaderVariantCollectionBuilder.NormalizeAssetPath(folder);
|
||||
if (string.IsNullOrEmpty(folder))
|
||||
return;
|
||||
if (!m_SearchFolders.Contains(folder))
|
||||
m_SearchFolders.Add(folder);
|
||||
}
|
||||
|
||||
private static string PickProjectFolder(string current)
|
||||
{
|
||||
var start = AssetDatabase.IsValidFolder(current) ? current : "Assets";
|
||||
var absoluteStart = NBShaderVariantCollectionBuilder.ToAbsolutePath(start);
|
||||
var picked = EditorUtility.OpenFolderPanel("Select Project Folder", absoluteStart, string.Empty);
|
||||
return NBShaderVariantCollectionBuilder.NormalizeAssetPath(picked);
|
||||
}
|
||||
|
||||
private bool HasSelectedTier()
|
||||
{
|
||||
return (m_TierMask & AllTierMask) != 0;
|
||||
}
|
||||
|
||||
private bool IsTierSelected(NBShaderFeatureTier tier)
|
||||
{
|
||||
return (m_TierMask & (1 << (int)tier)) != 0;
|
||||
}
|
||||
|
||||
private NBShaderFeatureTier[] GetSelectedTiers()
|
||||
{
|
||||
var result = new List<NBShaderFeatureTier>();
|
||||
for (var i = 0; i < Tiers.Length; i++)
|
||||
{
|
||||
var tier = Tiers[i];
|
||||
if (IsTierSelected(tier))
|
||||
result.Add(tier);
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
private void SetTierMask(int tierMask)
|
||||
{
|
||||
tierMask &= AllTierMask;
|
||||
if (m_TierMask == tierMask)
|
||||
return;
|
||||
|
||||
m_TierMask = tierMask;
|
||||
m_Previews = new NBShaderVariantCollectionTierResult[0];
|
||||
m_Status = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b1a45eb1f5ea442aa4193608a1b355a3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,474 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using NBShader;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace NBShaders2.Editor.FeatureLevel
|
||||
{
|
||||
public static class NBShaderVariantCollectionBuilder
|
||||
{
|
||||
public const string DefaultOutputFolder = "Assets/NBShader/ShaderVariantCollections";
|
||||
|
||||
private static readonly NBShaderFeatureTier[] Tiers =
|
||||
{
|
||||
NBShaderFeatureTier.Low,
|
||||
NBShaderFeatureTier.Medium,
|
||||
NBShaderFeatureTier.High,
|
||||
NBShaderFeatureTier.Ultra
|
||||
};
|
||||
|
||||
public static NBShaderVariantCollectionBuildResult Preview(IEnumerable<string> searchFolders, string outputFolder)
|
||||
{
|
||||
return Preview(searchFolders, outputFolder, null);
|
||||
}
|
||||
|
||||
public static NBShaderVariantCollectionBuildResult Preview(
|
||||
IEnumerable<string> searchFolders,
|
||||
string outputFolder,
|
||||
IEnumerable<NBShaderFeatureTier> tiers)
|
||||
{
|
||||
var validFolders = GetValidSearchFolders(searchFolders);
|
||||
var materials = CollectMaterialsInValidFolders(validFolders);
|
||||
return BuildResult(validFolders, materials, NormalizeOutputFolder(outputFolder), NormalizeTiers(tiers), false);
|
||||
}
|
||||
|
||||
public static NBShaderVariantCollectionBuildResult Generate(IEnumerable<string> searchFolders, string outputFolder)
|
||||
{
|
||||
return Generate(searchFolders, outputFolder, null);
|
||||
}
|
||||
|
||||
public static NBShaderVariantCollectionBuildResult Generate(
|
||||
IEnumerable<string> searchFolders,
|
||||
string outputFolder,
|
||||
IEnumerable<NBShaderFeatureTier> tiers)
|
||||
{
|
||||
var validFolders = GetValidSearchFolders(searchFolders);
|
||||
var materials = CollectMaterialsInValidFolders(validFolders);
|
||||
var normalizedOutputFolder = NormalizeOutputFolder(outputFolder);
|
||||
var normalizedTiers = NormalizeTiers(tiers);
|
||||
|
||||
if (normalizedTiers.Length == 0)
|
||||
return BuildResult(validFolders, materials, normalizedOutputFolder, normalizedTiers, false, "No NBShader feature tiers were selected.");
|
||||
|
||||
if (materials.Length == 0)
|
||||
return BuildResult(validFolders, materials, normalizedOutputFolder, normalizedTiers, false, "No NBShader materials were found in the selected folders.");
|
||||
|
||||
string errorMessage;
|
||||
if (!EnsureOutputFolder(normalizedOutputFolder, out errorMessage))
|
||||
return BuildResult(validFolders, materials, normalizedOutputFolder, normalizedTiers, false, errorMessage);
|
||||
|
||||
return BuildResult(validFolders, materials, normalizedOutputFolder, normalizedTiers, true);
|
||||
}
|
||||
|
||||
public static Material[] CollectMaterials(IEnumerable<string> searchFolders)
|
||||
{
|
||||
var folders = GetValidSearchFolders(searchFolders);
|
||||
return CollectMaterialsInValidFolders(folders);
|
||||
}
|
||||
|
||||
private static Material[] CollectMaterialsInValidFolders(string[] folders)
|
||||
{
|
||||
if (folders.Length == 0)
|
||||
return new Material[0];
|
||||
|
||||
var guids = AssetDatabase.FindAssets("t:Material", folders);
|
||||
var result = new List<Material>();
|
||||
var seen = new HashSet<string>(StringComparer.Ordinal);
|
||||
for (var i = 0; i < guids.Length; i++)
|
||||
{
|
||||
var path = AssetDatabase.GUIDToAssetPath(guids[i]);
|
||||
if (string.IsNullOrEmpty(path) || !seen.Add(path))
|
||||
continue;
|
||||
|
||||
var material = AssetDatabase.LoadAssetAtPath<Material>(path);
|
||||
if (NBShaderMaterialIntentResolver.IsNBShaderMaterial(material))
|
||||
result.Add(material);
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
public static string[] GetValidSearchFolders(IEnumerable<string> searchFolders)
|
||||
{
|
||||
if (searchFolders == null)
|
||||
return new string[0];
|
||||
|
||||
var result = new List<string>();
|
||||
var seen = new HashSet<string>(StringComparer.Ordinal);
|
||||
foreach (var searchFolder in searchFolders)
|
||||
{
|
||||
var path = NormalizeAssetPath(searchFolder);
|
||||
if (string.IsNullOrEmpty(path) || !AssetDatabase.IsValidFolder(path) || !seen.Add(path))
|
||||
continue;
|
||||
|
||||
result.Add(path);
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
internal static string NormalizeAssetPath(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return string.Empty;
|
||||
|
||||
path = path.Replace('\\', '/').Trim();
|
||||
if (path.StartsWith("Assets", StringComparison.Ordinal) ||
|
||||
path.StartsWith("Packages", StringComparison.Ordinal))
|
||||
{
|
||||
return path.TrimEnd('/');
|
||||
}
|
||||
|
||||
var projectRoot = Path.GetFullPath(Path.Combine(Application.dataPath, "..")).Replace('\\', '/').TrimEnd('/');
|
||||
var fullPath = Path.GetFullPath(path).Replace('\\', '/').TrimEnd('/');
|
||||
if (fullPath.StartsWith(projectRoot + "/", StringComparison.OrdinalIgnoreCase))
|
||||
return fullPath.Substring(projectRoot.Length + 1);
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
internal static string ToAbsolutePath(string assetPath)
|
||||
{
|
||||
assetPath = NormalizeAssetPath(assetPath);
|
||||
var projectRoot = Path.GetFullPath(Path.Combine(Application.dataPath, "..")).Replace('\\', '/').TrimEnd('/');
|
||||
if (string.IsNullOrEmpty(assetPath))
|
||||
return projectRoot;
|
||||
|
||||
return Path.Combine(projectRoot, assetPath).Replace('\\', '/');
|
||||
}
|
||||
|
||||
public static string GetOutputPath(string outputFolder, NBShaderFeatureTier tier)
|
||||
{
|
||||
outputFolder = NormalizeOutputFolder(outputFolder);
|
||||
if (string.IsNullOrEmpty(outputFolder))
|
||||
outputFolder = DefaultOutputFolder;
|
||||
return outputFolder.TrimEnd('/') + "/NBShader_" + tier + ".shadervariants";
|
||||
}
|
||||
|
||||
private static NBShaderVariantCollectionBuildResult BuildResult(
|
||||
string[] validFolders,
|
||||
Material[] materials,
|
||||
string outputFolder,
|
||||
NBShaderFeatureTier[] tiers,
|
||||
bool writeAssets,
|
||||
string errorMessage = null)
|
||||
{
|
||||
if (tiers == null)
|
||||
tiers = CloneDefaultTiers();
|
||||
|
||||
var tierResults = new NBShaderVariantCollectionTierResult[tiers.Length];
|
||||
for (var i = 0; i < tiers.Length; i++)
|
||||
{
|
||||
var tier = tiers[i];
|
||||
var buildInfo = NBShaderFeatureLevelEditorAPI.GetBuildInfo(
|
||||
tier,
|
||||
materials,
|
||||
NBShaderBuildInfoMode.ExactMaterialVariants);
|
||||
var variants = BuildSvcVariants(buildInfo);
|
||||
var path = GetOutputPath(outputFolder, tier);
|
||||
var generated = false;
|
||||
string tierError = null;
|
||||
|
||||
if (writeAssets)
|
||||
generated = WriteCollection(path, variants, out tierError);
|
||||
|
||||
tierResults[i] = new NBShaderVariantCollectionTierResult(
|
||||
tier,
|
||||
buildInfo != null ? buildInfo.materials.Length : 0,
|
||||
buildInfo != null ? buildInfo.includedPassNames.Length : 0,
|
||||
variants.Length,
|
||||
path,
|
||||
generated,
|
||||
tierError);
|
||||
}
|
||||
|
||||
return new NBShaderVariantCollectionBuildResult(
|
||||
validFolders,
|
||||
materials,
|
||||
outputFolder,
|
||||
tierResults,
|
||||
errorMessage);
|
||||
}
|
||||
|
||||
private static SvcVariant[] BuildSvcVariants(NBShaderBuildInfoSet buildInfo)
|
||||
{
|
||||
if (buildInfo == null)
|
||||
return new SvcVariant[0];
|
||||
|
||||
var result = new List<SvcVariant>();
|
||||
var seen = new HashSet<string>(StringComparer.Ordinal);
|
||||
var variants = buildInfo.variants;
|
||||
for (var i = 0; i < variants.Length; i++)
|
||||
{
|
||||
var variant = variants[i];
|
||||
if (variant == null || variant.shader == null)
|
||||
continue;
|
||||
|
||||
var keywords = variant.keywords;
|
||||
var key = variant.shader.name + "|" + variant.passType + "|" + string.Join(";", keywords);
|
||||
if (!seen.Add(key))
|
||||
continue;
|
||||
|
||||
result.Add(new SvcVariant(variant.shader, variant.passType, keywords));
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
private static bool WriteCollection(string path, SvcVariant[] variants, out string errorMessage)
|
||||
{
|
||||
errorMessage = null;
|
||||
|
||||
var existing = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(path);
|
||||
var collection = existing as ShaderVariantCollection;
|
||||
if (existing != null && collection == null)
|
||||
{
|
||||
errorMessage = "Output path already exists and is not a ShaderVariantCollection:\n" + path;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (collection == null)
|
||||
{
|
||||
collection = new ShaderVariantCollection();
|
||||
AssetDatabase.CreateAsset(collection, path);
|
||||
}
|
||||
else
|
||||
{
|
||||
collection.Clear();
|
||||
}
|
||||
|
||||
for (var i = 0; i < variants.Length; i++)
|
||||
{
|
||||
var variant = variants[i];
|
||||
try
|
||||
{
|
||||
collection.Add(new ShaderVariantCollection.ShaderVariant(
|
||||
variant.shader,
|
||||
variant.passType,
|
||||
variant.keywords));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning("Failed to add NBShader SVC variant: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
EditorUtility.SetDirty(collection);
|
||||
AssetDatabase.SaveAssetIfDirty(collection);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool EnsureOutputFolder(string outputFolder, out string errorMessage)
|
||||
{
|
||||
errorMessage = null;
|
||||
if (string.IsNullOrEmpty(outputFolder))
|
||||
{
|
||||
errorMessage = "Output folder is empty.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AssetDatabase.IsValidFolder(outputFolder))
|
||||
return true;
|
||||
|
||||
if (!outputFolder.StartsWith("Assets/", StringComparison.Ordinal) &&
|
||||
!string.Equals(outputFolder, "Assets", StringComparison.Ordinal))
|
||||
{
|
||||
errorMessage = "Output folder must already exist unless it is under Assets:\n" + outputFolder;
|
||||
return false;
|
||||
}
|
||||
|
||||
var parts = outputFolder.Split('/');
|
||||
var current = parts[0];
|
||||
for (var i = 1; i < parts.Length; i++)
|
||||
{
|
||||
var next = current + "/" + parts[i];
|
||||
if (!AssetDatabase.IsValidFolder(next))
|
||||
AssetDatabase.CreateFolder(current, parts[i]);
|
||||
current = next;
|
||||
}
|
||||
|
||||
if (AssetDatabase.IsValidFolder(outputFolder))
|
||||
return true;
|
||||
|
||||
errorMessage = "Could not create output folder:\n" + outputFolder;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static string NormalizeOutputFolder(string outputFolder)
|
||||
{
|
||||
if (string.IsNullOrEmpty(outputFolder))
|
||||
return DefaultOutputFolder;
|
||||
|
||||
return NormalizeAssetPath(outputFolder);
|
||||
}
|
||||
|
||||
private static NBShaderFeatureTier[] NormalizeTiers(IEnumerable<NBShaderFeatureTier> tiers)
|
||||
{
|
||||
if (tiers == null)
|
||||
return CloneDefaultTiers();
|
||||
|
||||
var selected = new HashSet<NBShaderFeatureTier>();
|
||||
foreach (var tier in tiers)
|
||||
{
|
||||
if (IsValidTier(tier))
|
||||
selected.Add(tier);
|
||||
}
|
||||
|
||||
var result = new List<NBShaderFeatureTier>();
|
||||
for (var i = 0; i < Tiers.Length; i++)
|
||||
{
|
||||
var tier = Tiers[i];
|
||||
if (selected.Contains(tier))
|
||||
result.Add(tier);
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
private static bool IsValidTier(NBShaderFeatureTier tier)
|
||||
{
|
||||
return tier == NBShaderFeatureTier.Low ||
|
||||
tier == NBShaderFeatureTier.Medium ||
|
||||
tier == NBShaderFeatureTier.High ||
|
||||
tier == NBShaderFeatureTier.Ultra;
|
||||
}
|
||||
|
||||
private static NBShaderFeatureTier[] CloneDefaultTiers()
|
||||
{
|
||||
return (NBShaderFeatureTier[])Tiers.Clone();
|
||||
}
|
||||
|
||||
private sealed class SvcVariant
|
||||
{
|
||||
public readonly Shader shader;
|
||||
public readonly PassType passType;
|
||||
public readonly string[] keywords;
|
||||
|
||||
public SvcVariant(Shader shader, PassType passType, string[] keywords)
|
||||
{
|
||||
this.shader = shader;
|
||||
this.passType = passType;
|
||||
this.keywords = keywords != null ? (string[])keywords.Clone() : new string[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class NBShaderVariantCollectionBuildResult
|
||||
{
|
||||
private readonly string[] m_ValidSearchFolders;
|
||||
private readonly Material[] m_Materials;
|
||||
private readonly NBShaderVariantCollectionTierResult[] m_Tiers;
|
||||
|
||||
public readonly string outputFolder;
|
||||
public readonly string errorMessage;
|
||||
|
||||
public string[] validSearchFolders { get { return (string[])m_ValidSearchFolders.Clone(); } }
|
||||
public Material[] materials { get { return (Material[])m_Materials.Clone(); } }
|
||||
public NBShaderVariantCollectionTierResult[] tiers { get { return (NBShaderVariantCollectionTierResult[])m_Tiers.Clone(); } }
|
||||
public int materialCount { get { return m_Materials.Length; } }
|
||||
public int validSearchFolderCount { get { return m_ValidSearchFolders.Length; } }
|
||||
public int generatedCount { get { return CountGenerated(m_Tiers); } }
|
||||
public bool hasError { get { return !string.IsNullOrEmpty(firstErrorMessage); } }
|
||||
public bool hasTierError { get { return HasTierError(m_Tiers); } }
|
||||
public bool allTiersGenerated { get { return m_Tiers.Length > 0 && generatedCount == m_Tiers.Length; } }
|
||||
public bool succeeded { get { return !hasError && allTiersGenerated; } }
|
||||
public string firstErrorMessage { get { return GetFirstErrorMessage(errorMessage, m_Tiers); } }
|
||||
|
||||
public NBShaderVariantCollectionBuildResult(
|
||||
string[] validSearchFolders,
|
||||
Material[] materials,
|
||||
string outputFolder,
|
||||
NBShaderVariantCollectionTierResult[] tiers,
|
||||
string errorMessage)
|
||||
{
|
||||
m_ValidSearchFolders = validSearchFolders != null ? (string[])validSearchFolders.Clone() : new string[0];
|
||||
m_Materials = materials != null ? (Material[])materials.Clone() : new Material[0];
|
||||
this.outputFolder = outputFolder;
|
||||
m_Tiers = tiers != null ? (NBShaderVariantCollectionTierResult[])tiers.Clone() : new NBShaderVariantCollectionTierResult[0];
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
|
||||
private static int CountGenerated(NBShaderVariantCollectionTierResult[] tiers)
|
||||
{
|
||||
if (tiers == null || tiers.Length == 0)
|
||||
return 0;
|
||||
|
||||
var count = 0;
|
||||
for (var i = 0; i < tiers.Length; i++)
|
||||
{
|
||||
var tier = tiers[i];
|
||||
if (tier != null && tier.generated)
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
private static bool HasTierError(NBShaderVariantCollectionTierResult[] tiers)
|
||||
{
|
||||
if (tiers == null || tiers.Length == 0)
|
||||
return false;
|
||||
|
||||
for (var i = 0; i < tiers.Length; i++)
|
||||
{
|
||||
var tier = tiers[i];
|
||||
if (tier != null && tier.hasError)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static string GetFirstErrorMessage(string resultError, NBShaderVariantCollectionTierResult[] tiers)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(resultError))
|
||||
return resultError;
|
||||
|
||||
if (tiers == null || tiers.Length == 0)
|
||||
return null;
|
||||
|
||||
for (var i = 0; i < tiers.Length; i++)
|
||||
{
|
||||
var tier = tiers[i];
|
||||
if (tier != null && tier.hasError)
|
||||
return tier.errorMessage;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class NBShaderVariantCollectionTierResult
|
||||
{
|
||||
public readonly NBShaderFeatureTier tier;
|
||||
public readonly int materialCount;
|
||||
public readonly int passCount;
|
||||
public readonly int variantCount;
|
||||
public readonly string outputAssetPath;
|
||||
public readonly bool generated;
|
||||
public readonly string errorMessage;
|
||||
|
||||
public bool hasError { get { return !string.IsNullOrEmpty(errorMessage); } }
|
||||
|
||||
public NBShaderVariantCollectionTierResult(
|
||||
NBShaderFeatureTier tier,
|
||||
int materialCount,
|
||||
int passCount,
|
||||
int variantCount,
|
||||
string outputAssetPath,
|
||||
bool generated,
|
||||
string errorMessage)
|
||||
{
|
||||
this.tier = tier;
|
||||
this.materialCount = materialCount;
|
||||
this.passCount = passCount;
|
||||
this.variantCount = variantCount;
|
||||
this.outputAssetPath = outputAssetPath;
|
||||
this.generated = generated;
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f15d05bf7f574642927c7aed8465cff2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,114 @@
|
||||
using System.Collections.Generic;
|
||||
using NBShader;
|
||||
using UnityEditor.Build;
|
||||
using UnityEditor.Build.Reporting;
|
||||
using UnityEditor.Rendering;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace NBShaders2.Editor.FeatureLevel
|
||||
{
|
||||
public sealed class NBShaderVariantStripper : IPreprocessShaders, IPreprocessBuildWithReport
|
||||
{
|
||||
private static bool s_MissingExplicitTierWarningLogged;
|
||||
|
||||
public int callbackOrder { get { return 0; } }
|
||||
|
||||
public void OnPreprocessBuild(BuildReport report)
|
||||
{
|
||||
ResetMissingExplicitTierWarning();
|
||||
}
|
||||
|
||||
public void OnProcessShader(Shader shader, ShaderSnippetData snippet, IList<ShaderCompilerData> data)
|
||||
{
|
||||
if (shader == null || shader.name != NBShaderFeatureLevelCatalog.ShaderName || data == null || data.Count == 0)
|
||||
return;
|
||||
|
||||
NBShaderFeatureTier tier;
|
||||
if (!NBShaderFeatureLevelBuildStripOverride.TryGetCurrentTier(out tier))
|
||||
LogMissingExplicitTierWarning();
|
||||
|
||||
var projectSettings = NBShaderFeatureLevelProjectSettings.instance;
|
||||
HashSet<string> allowedKeywords = projectSettings.GetAllowedKeywordSetForBuildInfoNoSave(tier);
|
||||
HashSet<string> allowedPassFeatures = projectSettings.GetAllowedPassFeatureSetForBuildInfoNoSave(tier);
|
||||
|
||||
if (IsDisallowedManagedPass(snippet, allowedPassFeatures))
|
||||
{
|
||||
data.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = data.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (ContainsDisallowedManagedKeyword(shader, data[i], allowedKeywords))
|
||||
data.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
private static void LogMissingExplicitTierWarning()
|
||||
{
|
||||
if (s_MissingExplicitTierWarningLogged)
|
||||
return;
|
||||
|
||||
s_MissingExplicitTierWarningLogged = true;
|
||||
Debug.LogWarning(
|
||||
"NBShader2 build stripping tier was not specified. Defaulting to Ultra. " +
|
||||
"Wrap BuildPipeline.BuildPlayer or BuildPipeline.BuildAssetBundles in " +
|
||||
"NBShaderFeatureLevelEditorAPI.OverrideBuildStripExplicitTier(tier) to build lower-tier NBShader variants intentionally.");
|
||||
}
|
||||
|
||||
internal static void ResetMissingExplicitTierWarning()
|
||||
{
|
||||
s_MissingExplicitTierWarningLogged = false;
|
||||
}
|
||||
|
||||
private static bool IsDisallowedManagedPass(ShaderSnippetData snippet, HashSet<string> allowedPassFeatures)
|
||||
{
|
||||
if (allowedPassFeatures == null)
|
||||
return false;
|
||||
|
||||
string passFeatureId;
|
||||
if (!TryResolveManagedPassFeature(snippet, out passFeatureId))
|
||||
return false;
|
||||
|
||||
return !allowedPassFeatures.Contains(passFeatureId);
|
||||
}
|
||||
|
||||
private static bool TryResolveManagedPassFeature(ShaderSnippetData snippet, out string passFeatureId)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(snippet.passName) &&
|
||||
NBShaderFeatureLevelCatalog.TryGetManagedPassFeatureByPassName(snippet.passName, out passFeatureId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (snippet.passType)
|
||||
{
|
||||
case PassType.ScriptableRenderPipelineDefaultUnlit:
|
||||
passFeatureId = NBShaderPassFeatureCatalog.BackFirstPassId;
|
||||
return true;
|
||||
case PassType.ShadowCaster:
|
||||
passFeatureId = NBShaderPassFeatureCatalog.ShadowCasterPassId;
|
||||
return true;
|
||||
default:
|
||||
passFeatureId = string.Empty;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ContainsDisallowedManagedKeyword(Shader shader, ShaderCompilerData compilerData, HashSet<string> allowed)
|
||||
{
|
||||
var keywords = compilerData.shaderKeywordSet.GetShaderKeywords();
|
||||
for (var i = 0; i < keywords.Length; i++)
|
||||
{
|
||||
var keywordName = keywords[i].name;
|
||||
if (!NBShaderFeatureLevelCatalog.IsManagedKeyword(keywordName))
|
||||
continue;
|
||||
|
||||
if (allowed != null && !allowed.Contains(keywordName))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b503214133aa446e59281f9583f6a285
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Packages/NB_FX/NBShaders2/Editor/Localization.meta
Normal file
8
Packages/NB_FX/NBShaders2/Editor/Localization.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 624337c343f09c34091f162af2089afe
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,80 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBShaderEditor
|
||||
{
|
||||
[InitializeOnLoad]
|
||||
internal static class NBShaderInspectorLocalization
|
||||
{
|
||||
internal const string TableName = "NBShader";
|
||||
|
||||
private const string CsvAssetPath = "Packages/com.xuanxuan.nb.fx/NBShaders2/Editor/Localization/NBShaderInspectorLocalization.csv";
|
||||
|
||||
static NBShaderInspectorLocalization()
|
||||
{
|
||||
RegisterTable();
|
||||
}
|
||||
|
||||
public static string CurrentLanguage
|
||||
{
|
||||
get
|
||||
{
|
||||
RegisterTable();
|
||||
return ShaderGUILocalization.GetCurrentLanguage(TableName);
|
||||
}
|
||||
}
|
||||
|
||||
public static GUIContent MakeContent(string labelKey, string labelFallback, string tooltipKey = null, string tooltipFallback = "")
|
||||
{
|
||||
RegisterTable();
|
||||
return ShaderGUILocalization.MakeContent(TableName, labelKey, labelFallback, tooltipKey, tooltipFallback);
|
||||
}
|
||||
|
||||
public static GUIContent MakeInspectorContent(string key, string fallback, string tip = "")
|
||||
{
|
||||
RegisterTable();
|
||||
return ShaderGUILocalization.MakeInspectorContent(TableName, key, fallback, tip);
|
||||
}
|
||||
|
||||
public static string GetInspectorText(string key, string fallback = "")
|
||||
{
|
||||
RegisterTable();
|
||||
return ShaderGUILocalization.GetInspectorText(TableName, key, fallback);
|
||||
}
|
||||
|
||||
public static string[] GetInspectorOptions(string key, string[] fallback)
|
||||
{
|
||||
RegisterTable();
|
||||
return ShaderGUILocalization.GetInspectorOptions(TableName, key, fallback);
|
||||
}
|
||||
|
||||
public static string[] GetOptions(string keyPrefix, string[] fallback)
|
||||
{
|
||||
RegisterTable();
|
||||
return ShaderGUILocalization.GetOptions(TableName, keyPrefix, fallback);
|
||||
}
|
||||
|
||||
public static string Get(string key, string fallback = "")
|
||||
{
|
||||
RegisterTable();
|
||||
return ShaderGUILocalization.Get(TableName, key, fallback);
|
||||
}
|
||||
|
||||
public static string GetTooltip(string labelKey, string tooltipKey = null, string fallback = "")
|
||||
{
|
||||
RegisterTable();
|
||||
return ShaderGUILocalization.GetTooltip(TableName, labelKey, tooltipKey, fallback);
|
||||
}
|
||||
|
||||
public static void Reload()
|
||||
{
|
||||
RegisterTable();
|
||||
ShaderGUILocalization.Reload(TableName);
|
||||
}
|
||||
|
||||
private static void RegisterTable()
|
||||
{
|
||||
ShaderGUILocalization.RegisterCsv(TableName, CsvAssetPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8dd81afc56b7652468af814441ee910f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,805 @@
|
||||
key,zh-CN,en-US,zh-CN-tip,en-US-tip
|
||||
inspector.block.mode.label,模式设置,Mode,各种基础模式设置,Mode and source setup
|
||||
inspector.block.base.label,基本全局功能,Base,全局控制功能,Base global controls
|
||||
inspector.block.maintex.label,主贴图功能,Main Texture,主贴图以及主颜色相关功能,Main texture and base color controls
|
||||
inspector.block.light.label,光照功能,Lighting,法线、MatCap 和光照模式相关功能,Lighting normal and material response controls
|
||||
inspector.block.feature.label,特别功能,Feature,遮罩、扭曲、溶解等特效功能,Effect feature controls
|
||||
inspector.block.ta.label,TA调试,TA Debug,技术美术调试和辅助功能,Technical art debug and helper controls
|
||||
inspector.common.tilling.label,Tilling,Tilling,,
|
||||
inspector.common.offset.label,Offset,Offset,,
|
||||
inspector.common.none,None,None,,
|
||||
inspector.mode.meshSource.label,Mesh来源模式,Mesh Source Mode,Mesh来源模式和当前的对象类型一致,Matches the current object type
|
||||
inspector.mode.meshSource.option.0,粒子系统,Particle System,,
|
||||
inspector.mode.meshSource.option.1,模型(非粒子发射),Mesh,,
|
||||
inspector.mode.meshSource.option.2,2D RawImage,2D RawImage,,
|
||||
inspector.mode.meshSource.option.3,2D 精灵,2D Sprite,,
|
||||
inspector.mode.meshSource.option.4,2D 材质贴图,2D Material Texture,,
|
||||
inspector.mode.meshSource.option.5,2D UIParticle,2D UIParticle,,
|
||||
inspector.mode.transparent.label,透明模式,Transparent Mode,透明模式,Controls opaque transparent and cutoff rendering
|
||||
inspector.mode.transparent.option.0,不透明Opaque,Opaque,,
|
||||
inspector.mode.transparent.option.1,半透明Transparent,Transparent,,
|
||||
inspector.mode.transparent.option.2,不透明裁剪CutOff,CutOff,,
|
||||
inspector.mode.cutoff.label,裁剪位置,Cutoff,0为完全不裁剪,1为完全裁剪,0 keeps everything and 1 clips everything
|
||||
inspector.mode.blend.label,混合模式,Blend Mode,,
|
||||
inspector.mode.blend.option.0,透明度混合AlphaBlend,Alpha Blend,,
|
||||
inspector.mode.blend.option.1,预乘PreMultiply,Premultiply,,
|
||||
inspector.mode.blend.option.2,叠加Additive,Additive,,
|
||||
inspector.mode.blend.option.3,正片叠底Multiply,Multiply,,
|
||||
inspector.mode.additiveToPremultiply.label,叠加到预乘混合,Additive To Premultiply,0为叠加混合,1为预乘混合,0 is additive and 1 is premultiply
|
||||
inspector.base.colorIntensity.label,整体颜色强度,Base Color Intensity,,
|
||||
inspector.base.alphaAll.label,整体透明度,Overall Alpha,,
|
||||
inspector.base.colorAdjustment.label,颜色调整,Color Adjustment,,
|
||||
inspector.base.colorAdjustment.onlyMainTex.label,颜色调整仅影响主贴图,Color Adjustment Only Affects Main Texture,,
|
||||
inspector.base.hueShift.label,色相偏移,Hue Shift,,
|
||||
inspector.base.hueShift.value.label,色相,Hue,,
|
||||
inspector.base.hueShift.customData.label,色相自定义曲线,Hue Custom Data,,
|
||||
inspector.base.saturability.label,饱和度,Saturation,,
|
||||
inspector.base.saturability.value.label,饱和度,Saturation,,
|
||||
inspector.base.saturability.customData.label,饱和度强度自定义曲线,Saturation Strength Custom Data,,
|
||||
inspector.base.contrast.label,对比度,Contrast,,
|
||||
inspector.base.contrast.mid.label,对比度中值颜色,Contrast Mid Color,,
|
||||
inspector.base.contrast.value.label,对比度,Contrast,,
|
||||
inspector.base.contrast.customData.label,对比度自定义曲线,Contrast Custom Data,,
|
||||
inspector.base.colorRefine.label,颜色修正,Color Refine,,
|
||||
inspector.base.colorRefine.a.label,A:主颜色相乘,A Main Color Multiply,,
|
||||
inspector.base.colorRefine.bPower.label,B:主颜色Power,B Main Color Power,,
|
||||
inspector.base.colorRefine.bMultiply.label,B:主颜色Power后相乘,B After Power Multiply,,
|
||||
inspector.base.colorRefine.lerp.label,A/B线性差值,A/B Lerp,,
|
||||
inspector.base.colorMultiAlpha.label,颜色乘透明度(改善锯齿),Color Multiply Alpha,,
|
||||
inspector.base.ztest.label,深度测试,ZTest,,
|
||||
inspector.base.ztest.option.0,关闭(Disabled),Disabled,,
|
||||
inspector.base.ztest.option.1,永不通过(Never),Never,,
|
||||
inspector.base.ztest.option.2,小于(Less),Less,,
|
||||
inspector.base.ztest.option.3,等于(Equal),Equal,,
|
||||
inspector.base.ztest.option.4,小于等于(LessEqual),LessEqual,,
|
||||
inspector.base.ztest.option.5,大于(Greater),Greater,,
|
||||
inspector.base.ztest.option.6,不等于(NotEqual),NotEqual,,
|
||||
inspector.base.ztest.option.7,大于等于(GreaterEqual),GreaterEqual,,
|
||||
inspector.base.ztest.option.8,始终通过(Always),Always,,
|
||||
inspector.base.cull.label,渲染面向,Cull,,
|
||||
inspector.base.cull.option.0,双面(Both),Both,,
|
||||
inspector.base.cull.option.1,背面(Back),Back,,
|
||||
inspector.base.cull.option.2,正面(Front),Front,,
|
||||
inspector.base.backFirstPass.label,预渲染反面,Back First Pass,,
|
||||
inspector.base.forceZWrite.label,深度写入强制控制,Force ZWrite,,
|
||||
inspector.base.forceZWrite.option.0,默认,Default,,
|
||||
inspector.base.forceZWrite.option.1,强制开启,Force On,,
|
||||
inspector.base.forceZWrite.option.2,强制关闭,Force Off,,
|
||||
inspector.base.affectsShadows.label,投射阴影,Affects Shadows,,
|
||||
inspector.base.transparentShadowDither.label,半透明 Dither 阴影,Transparent Dither Shadows,,
|
||||
inspector.base.backColor.label,背面颜色,Back Color,,
|
||||
inspector.base.backColor.color.label,背面颜色,Back Color,,
|
||||
inspector.base.distanceFade.label,近距离透明,Distance Fade,,
|
||||
inspector.base.distanceFade.range.label,透明过度范围,Fade Range,,
|
||||
inspector.base.softParticles.label,软粒子,Soft Particles,,
|
||||
inspector.base.softParticles.range.label,远近裁剪面,Near/Far Fade,,
|
||||
inspector.base.stencilWithoutPlayer.label,剔除主角色,Stencil Without Player,,
|
||||
inspector.base.ignoreVertexColor.label,忽略顶点色,Ignore Vertex Color,,
|
||||
inspector.base.fogIntensity.label,雾影响强度,Fog Intensity,,
|
||||
inspector.maintex.basemap.label,主贴图,Main Texture,,
|
||||
inspector.maintex.basemap.related.label,主贴图相关功能,Main Texture Related,,
|
||||
inspector.maintex.graphic.message,当前模式使用 Graphic 贴图。仅颜色和 ST 可编辑。,Current mode uses Graphic texture. Only color and ST remain editable.,,
|
||||
inspector.maintex.uicolor.label,贴图颜色叠加,Graphic Color,,
|
||||
inspector.maintex.alphaChannel.label,主贴图透明度通道,Alpha Channel,,
|
||||
inspector.maintex.wrap.label,主贴图循环模式,Main Texture Wrap,,
|
||||
inspector.maintex.uvmode.label,主贴图UV来源,Main Texture UV Source,,
|
||||
inspector.maintex.customdata.offsetx.label,主贴图X轴偏移自定义曲线,Main Texture Offset X Custom Data,,
|
||||
inspector.maintex.customdata.offsety.label,主贴图Y轴偏移自定义曲线,Main Texture Offset Y Custom Data,,
|
||||
inspector.maintex.offsetspeed.label,偏移速度,Offset Speed,,
|
||||
inspector.maintex.rotation.label,主贴图旋转,Rotation,,
|
||||
inspector.maintex.rotationspeed.label,主贴图旋转速度,Rotation Speed,,
|
||||
inspector.maintex.distortionIntensity.label,主贴图扭曲强度控制,Main Texture Distortion,,
|
||||
inspector.maintex.pnoiseBlend.label,主贴图程序噪波混合,Main Texture Program Noise Blend,,
|
||||
inspector.maintex.mixedMeshSource.message,混合的 Mesh 来源模式会按材质状态显示对应贴图控件。,Mixed mesh-source modes will show the matching texture controls per material state.,,
|
||||
inspector.light.mode.label,光照类型,Light Mode,,
|
||||
inspector.light.mode.option.0,默认无光(Unlit),Unlit,,
|
||||
inspector.light.mode.option.1,简单光照(BlinnPhong),BlinnPhong,,
|
||||
inspector.light.mode.option.2,简单光照通透(HalfLambert),HalfLambert,,
|
||||
inspector.light.mode.option.3,高级光照(PBR),PBR,,
|
||||
inspector.light.mode.option.4,六路光照(SixWay),SixWay,,
|
||||
inspector.light.specular.toggle.label,高光开关,Specular,,
|
||||
inspector.light.specular.color.label,高光颜色,Specular Color,,
|
||||
inspector.light.specular.smoothness.label,光滑度,Smoothness,,
|
||||
inspector.light.pbr.metallic.label,金属度,Metallic,,
|
||||
inspector.light.pbr.smoothness.label,光滑度,Smoothness,,
|
||||
inspector.light.bump.toggle.label,法线贴图开关,Normal Map,,
|
||||
inspector.light.bump.texture.label,法线贴图,Normal Map,,
|
||||
inspector.light.bump.related.label,法线贴图相关功能,Normal Map Related,,
|
||||
inspector.light.bump.wrap.label,法线贴图循环模式,Normal Map Wrap,,
|
||||
inspector.light.bump.uvmode.label,法线贴图UV来源,Normal Map UV Source,,
|
||||
inspector.light.bump.maskMode.label,法线贴图多通道模式,Normal Map Multi Channel,,
|
||||
inspector.light.bump.scale.label,法线强度,Normal Strength,,
|
||||
inspector.light.matcap.toggle.label,MatCap模拟材质,MatCap,,
|
||||
inspector.light.matcap.texture.label,MatCap图,MatCap Texture,,
|
||||
inspector.light.matcap.blend.label,MatCap相加到相乘过渡,Add/Multiply Blend,,
|
||||
inspector.light.sixway.positive.label,六路正方向图(P),SixWay Positive,,
|
||||
inspector.light.sixway.negative.label,六路反方向图(N),SixWay Negative,,
|
||||
inspector.light.sixway.absorption.toggle.label,光照颜色吸收,Light Color Absorption,,
|
||||
inspector.light.sixway.absorption.strength.label,六路吸收强度,Absorption Strength,,
|
||||
inspector.light.sixway.ramp.label,六路自发光Ramp,SixWay Emission Ramp,,
|
||||
inspector.light.sixway.emissionPow.label,六路自发光Pow,SixWay Emission Pow,,
|
||||
inspector.light.sixway.color.label,六路自发光颜色,SixWay Emission Color,,
|
||||
inspector.protocol.colorChannel.option.0,R,R,,
|
||||
inspector.protocol.colorChannel.option.1,G,G,,
|
||||
inspector.protocol.colorChannel.option.2,B,B,,
|
||||
inspector.protocol.colorChannel.option.3,A,A,,
|
||||
inspector.protocol.customData.option.0,**不使用**,Off,,
|
||||
inspector.protocol.customData.option.1,CustomData1_X,CustomData1.x,,
|
||||
inspector.protocol.customData.option.2,CustomData1_Y,CustomData1.y,,
|
||||
inspector.protocol.customData.option.3,CustomData1_Z,CustomData1.z,,
|
||||
inspector.protocol.customData.option.4,CustomData1_W,CustomData1.w,,
|
||||
inspector.protocol.customData.option.5,CustomData2_X,CustomData2.x,,
|
||||
inspector.protocol.customData.option.6,CustomData2_Y,CustomData2.y,,
|
||||
inspector.protocol.customData.option.7,CustomData2_Z,CustomData2.z,,
|
||||
inspector.protocol.customData.option.8,CustomData2_W,CustomData2.w,,
|
||||
inspector.protocol.wrapMode.option.0,Repeat,Repeat,,
|
||||
inspector.protocol.wrapMode.option.1,Clamp,Clamp,,
|
||||
inspector.protocol.wrapMode.option.2,RepeatX_ClampY,RepeatX_ClampY,,
|
||||
inspector.protocol.wrapMode.option.3,ClampX_RepeatY,ClampX_RepeatY,,
|
||||
inspector.protocol.pnoise.opacity.label,程序噪波混合强度,Program Noise Blend Opacity,,
|
||||
inspector.protocol.pnoiseBlend.option.0,不使用,Not Used,,
|
||||
inspector.protocol.pnoiseBlend.option.1,Multiply,Multiply,,
|
||||
inspector.protocol.pnoiseBlend.option.2,Min,Min,,
|
||||
inspector.protocol.pnoiseBlend.option.3,HardLight,HardLight,,
|
||||
inspector.protocol.uv.twirl.label,旋转扭曲,Twirl,,
|
||||
inspector.protocol.uv.twirlCenter.label,旋转扭曲中心,Twirl Center,,
|
||||
inspector.protocol.uv.twirlStrength.label,旋转扭曲强度,Twirl Strength,,
|
||||
inspector.protocol.uv.polar.label,极坐标,Polar Coordinates,,
|
||||
inspector.protocol.uv.polarCenter.label,极坐标中心,Polar Center,,
|
||||
inspector.protocol.uv.polarStrength.label,极坐标强度,Polar Strength,,
|
||||
inspector.protocol.uv.cylinderRotate.label,圆柱坐标旋转,Cylinder Rotation,,
|
||||
inspector.protocol.uv.cylinderOffset.label,圆柱坐标偏移,Cylinder Offset,,
|
||||
inspector.protocol.uv.coordinatePlane.label,坐标平面,Coordinate Plane,,
|
||||
inspector.protocol.uv.sharedMaterial.message,以下设置材质内通用:,The following settings are shared in the material:,,
|
||||
inspector.protocol.uv.cylinderWarning.message,圆柱模式消耗比较大,慎用,Cylinder mode is expensive. Use it carefully.,,
|
||||
inspector.protocol.uv.mode.option.0,默认UV通道,Default UV Channel,,
|
||||
inspector.protocol.uv.mode.option.1,特殊UV通道,Special UV Channel,,
|
||||
inspector.protocol.uv.mode.option.2,极坐标|旋转,Polar/Twirl,,
|
||||
inspector.protocol.uv.mode.option.3,圆柱无缝,Cylinder Seamless,,
|
||||
inspector.protocol.uv.mode.option.4,主贴图,Main Texture,,
|
||||
inspector.protocol.uv.mode.option.5,屏幕UV,Screen UV,,
|
||||
inspector.protocol.uv.mode.option.6,世界坐标,World Space,,
|
||||
inspector.protocol.uv.mode.option.7,局部本地坐标,Object Space,,
|
||||
inspector.protocol.uv.mode.option.8,公共UV,Shared UV,,
|
||||
inspector.protocol.uv.positionPlane.option.0,xy平面,XY Plane,,
|
||||
inspector.protocol.uv.positionPlane.option.1,xz平面,XZ Plane,,
|
||||
inspector.protocol.uv.positionPlane.option.2,yz平面,YZ Plane,,
|
||||
inspector.protocol.uv.specialChannel.label,特殊UV通道选择,Special UV Channel,,
|
||||
inspector.protocol.uv.specialChannel.option.0,UV2_Texcoord1,UV2_Texcoord1,,
|
||||
inspector.protocol.uv.specialChannel.option.1,UV3_Texcoord2,UV3_Texcoord2,,
|
||||
inspector.ta.zoffset.label,深度偏移,Z Offset,,
|
||||
inspector.ta.overrideZ.label,Override Z,Override Z,,
|
||||
inspector.ta.renderQueue.label,渲染队列偏移,Queue Bias,,
|
||||
inspector.ta.customStencil.label,模板手动调试开关,Custom Stencil Test,,
|
||||
inspector.ta.keywords.label,已开启Keyword:,Enabled Keywords,,
|
||||
inspector.ta.keywords.list.label,已开启Keyword:,Enabled Keywords,,
|
||||
inspector.ta.property._offsetFactor.label,OffsetFactor,Offset Factor,,
|
||||
inspector.ta.property._offsetUnits.label,Offset单位,Offset Units,,
|
||||
inspector.ta.property._OverrideZValue.label,Override Z Value,Override Z Value,,
|
||||
inspector.ta.property._ColorMask.label,RGBA Mask,RGBA Mask,控制最终写入 RenderTarget 的颜色通道,Controls final RenderTarget color channel writes
|
||||
inspector.ta.colorMask.option.0,R,R,,
|
||||
inspector.ta.colorMask.option.1,G,G,,
|
||||
inspector.ta.colorMask.option.2,B,B,,
|
||||
inspector.ta.colorMask.option.3,A,A,,
|
||||
inspector.ta.property._StencilKeyIndex.label,Stencil配置索引,Stencil Config Index,,
|
||||
inspector.ta.property._Stencil.label,模板值,Stencil Value,,
|
||||
inspector.ta.property._StencilComp.label,模板比较方式,Stencil Compare,,
|
||||
inspector.ta.property._StencilOp.label,模板处理方式,Stencil Operation,,
|
||||
inspector.ta.property._StencilFail.label,模板失败处理方式,Stencil Fail,,
|
||||
inspector.ta.property._StencilZFail.label,模板深度失败处理方式,Stencil ZFail,,
|
||||
inspector.ta.property._StencilReadMask.label,模板读取掩码,Stencil Read Mask,,
|
||||
inspector.ta.property._StencilWriteMask.label,模板写入掩码,Stencil Write Mask,,
|
||||
inspector.vertexStreams.title.label,顶点流统计,Particle Vertex Streams,The vertex streams needed for this Material to function properly.,The vertex streams needed for this Material to function properly.
|
||||
inspector.vertexStreams.mismatch,下面的粒子系统Renderer顶点流不正确:,Particle renderers with mismatched vertex streams:,,
|
||||
inspector.vertexStreams.trailMismatch,下面的粒子系统Renderer拖尾顶点流不正确:,Particle trail renderers with mismatched vertex streams:,,
|
||||
inspector.vertexStreams.applyTrail.button,使粒子拖尾与材质顶点流相同,Apply Trail Vertex Streams,Apply the trail vertex stream layout to all Particle Systems using this material,Apply the trail vertex stream layout to all Particle Systems using this material
|
||||
inspector.vertexStreams.apply.button,使粒子与材质顶点流相同,Apply Vertex Streams,Apply the vertex stream layout to all Particle Systems using this material,Apply the vertex stream layout to all Particle Systems using this material
|
||||
inspector.vertexStreams.apply.undo,Apply custom vertex streams from material,Apply custom vertex streams from material,,
|
||||
inspector.feature.popup._ScreenDistortModeToggle.option.0,不扰动屏幕,No Screen Distort,,
|
||||
inspector.feature.popup._ScreenDistortModeToggle.option.1,全部扰动(后处理扰动),Deferred Distort,,
|
||||
inspector.feature.popup._ScreenDistortModeToggle.option.2,仅影响场景(半透明前扰动),Camera Opaque Distort,,
|
||||
inspector.feature.screenDistort.missingNbPostProcess.message,屏幕扭曲需要当前 URP Pipeline Asset 的任意 RendererData 中包含已启用的 NB 后处理 Feature。,Screen Distort requires at least one RendererData in the current URP Pipeline Asset to include an active NB Post Process Feature.,,
|
||||
inspector.feature.screenDistort.missingOpaqueTextureCopy.message,Camera Opaque 扭曲需要在当前 URP Pipeline Asset 中开启 Opaque Texture。,Camera Opaque Distort requires Opaque Texture to be enabled on the current URP Pipeline Asset.,,
|
||||
inspector.feature.screenDistort.pingRendererData.button,定位 Renderer,Ping Renderer,,
|
||||
inspector.feature.screenDistort.pingPipelineAsset.button,定位 Pipeline Asset,Ping Pipeline Asset,,
|
||||
inspector.feature.popup._DistortMode.option.0,扰动贴图,FlowMap/RG Texture,,
|
||||
inspector.feature.popup._DistortMode.option.1,折射,Refraction IOR,,
|
||||
inspector.feature.popup._ColorBlendAlphaMultiplyMode.option.0,颜色渐变强度,Off,,
|
||||
inspector.feature.popup._ColorBlendAlphaMultiplyMode.option.1,遮罩(乘以主贴图Alpha),On,,
|
||||
inspector.feature.popup._RampColorSourceMode.option.0,UV,Gradient,,
|
||||
inspector.feature.popup._RampColorSourceMode.option.1,映射贴图,Texture,,
|
||||
inspector.feature.popup._RampColorBlendMode.option.0,相乘Multiply,Add,,
|
||||
inspector.feature.popup._RampColorBlendMode.option.1,相加Add,Multiply,,
|
||||
inspector.feature.popup._DissolveRampSourceMode.option.0,渐变控件,Gradient,,
|
||||
inspector.feature.popup._DissolveRampSourceMode.option.1,Ramp贴图,Texture,,
|
||||
inspector.feature.popup._DissolveRampColorBlendMode.option.0,线性差值Lerp,Add,,
|
||||
inspector.feature.popup._DissolveRampColorBlendMode.option.1,相乘Multiply,Multiply,,
|
||||
inspector.feature.popup._DissolveMaskMode.option.0,过程溶解,Process Dissolve,,
|
||||
inspector.feature.popup._DissolveMaskMode.option.1,溶解遮罩,Dissolve Mask,,
|
||||
inspector.feature.popup._FresnelMode.option.0,颜色|边缘光,Color,,
|
||||
inspector.feature.popup._FresnelMode.option.1,半透明|渐隐,Alpha,,
|
||||
inspector.feature.popup._VATMode.option.0,Houdini,Houdini,,
|
||||
inspector.feature.popup._VATMode.option.1,TyFlow,TyFlow,,
|
||||
inspector.feature.popup._HoudiniVATSubMode.option.0,SoftBody (Deformation),SoftBody (Deformation),,
|
||||
inspector.feature.popup._HoudiniVATSubMode.option.1,RigidBody (Pieces),RigidBody (Pieces),,
|
||||
inspector.feature.popup._HoudiniVATSubMode.option.2,Dynamic Remeshing (Lookup),Dynamic Remeshing (Lookup),,
|
||||
inspector.feature.popup._HoudiniVATSubMode.option.3,Particle Sprites (Billboard),Particle Sprites (Billboard),,
|
||||
inspector.feature.popup._TyFlowVATSubMode.option.0,Absolute positions,Absolute positions,,
|
||||
inspector.feature.popup._TyFlowVATSubMode.option.1,Relative offsets,Relative offsets,,
|
||||
inspector.feature.popup._TyFlowVATSubMode.option.2,Skin (R),Skin (R),,
|
||||
inspector.feature.popup._TyFlowVATSubMode.option.3,Skin (PR),Skin (PR),,
|
||||
inspector.feature.popup._TyFlowVATSubMode.option.4,Skin (PRSAVE),Skin (PRSAVE),,
|
||||
inspector.feature.popup._TyFlowVATSubMode.option.5,Skin (PRSXYZ),Skin (PRSXYZ),,
|
||||
inspector.feature.popup._MaskMapGradientToggle.option.0,遮罩贴图,Texture,,
|
||||
inspector.feature.popup._MaskMapGradientToggle.option.1,渐变控件,Gradient,,
|
||||
inspector.feature.popup._MaskMap2GradientToggle.option.0,遮罩贴图,Texture,,
|
||||
inspector.feature.popup._MaskMap2GradientToggle.option.1,渐变控件,Gradient,,
|
||||
inspector.feature.popup._MaskMap3GradientToggle.option.0,遮罩贴图,Texture,,
|
||||
inspector.feature.popup._MaskMap3GradientToggle.option.1,渐变控件,Gradient,,
|
||||
inspector.feature.vat.houdiniUnsupportedParticle.message,该 Houdini VAT 类型需要 Mesh 多 UV 数据,不支持 ParticleSystem VertexStream 模式。,This Houdini VAT type requires mesh multi-UV data and does not support ParticleSystem VertexStream mode.,,
|
||||
inspector.feature.vat.tyflowUnsupportedParticle.message,该 TyFlow VAT 类型需要 Mesh 多 UV 数据,不支持 ParticleSystem VertexStream 模式。,This TyFlow VAT type requires mesh multi-UV data and does not support ParticleSystem VertexStream mode.,,
|
||||
inspector.feature.vat.tyflowUv2Conflict.message,TyFlow VAT 在粒子模式下使用 UV2 (TEXCOORD0.zw) 作为 vertexIndex / vertexCount。序列帧融帧或特殊 UV (UV2) 会冲突,VAT 优先。,TyFlow VAT uses UV2 (TEXCOORD0.zw) as vertexIndex / vertexCount in ParticleSystem mode. Flipbook blending or Special UV (UV2) conflicts with it; VAT takes priority.,,
|
||||
inspector.feature.遮罩.label,遮罩,Mask,,
|
||||
inspector.feature.测试遮罩颜色.label,测试遮罩颜色,Debug Mask Color,,
|
||||
inspector.feature.遮罩强度.label,遮罩强度,Mask Strength,,
|
||||
inspector.feature.遮罩整体调整.label,遮罩整体调整,Mask Refine,,
|
||||
inspector.feature.范围(Pow).label,范围(Pow),Range (Pow),,
|
||||
inspector.feature.相乘.label,相乘,Multiply,,
|
||||
inspector.feature.偏移(相加).label,偏移(相加),Offset (Add),,
|
||||
inspector.feature.遮罩程序噪波混合.label,遮罩程序噪波混合,Mask Program Noise Blend,,
|
||||
inspector.feature.遮罩模式.label,遮罩模式,Mask Mode,,
|
||||
inspector.feature.遮罩贴图.label,遮罩贴图,Mask Texture,,
|
||||
inspector.feature.遮罩通道选择.label,遮罩通道选择,Mask Channel,,
|
||||
inspector.feature.遮罩渐变.label,遮罩渐变,Mask Gradient,,
|
||||
inspector.feature.遮罩UV Wrap.label,遮罩循环模式,Mask UV Wrap,,
|
||||
inspector.feature.遮罩UV来源.label,遮罩UV来源,Mask UV Source,,
|
||||
inspector.feature.Mask图X轴偏移自定义曲线.label,Mask图X轴偏移自定义曲线,Mask Offset X Custom Data,,
|
||||
inspector.feature.Mask图Y轴偏移自定义曲线.label,Mask图Y轴偏移自定义曲线,Mask Offset Y Custom Data,,
|
||||
inspector.feature.遮罩偏移速度.label,遮罩偏移速度,Mask Offset Speed,,
|
||||
inspector.feature.遮罩旋转.label,遮罩旋转,Mask Rotation,,
|
||||
inspector.feature.遮罩旋转速度.label,遮罩旋转速度,Mask Rotation Speed,,
|
||||
inspector.feature.旋转速度.label,旋转速度,Rotation Speed,,
|
||||
inspector.feature.遮罩扭曲强度.label,遮罩扭曲强度,Mask Distortion Strength,,
|
||||
inspector.feature.遮罩2.label,遮罩2,Mask 2,,
|
||||
inspector.feature.遮罩2模式.label,遮罩2模式,Mask 2 Mode,,
|
||||
inspector.feature.遮罩2贴图.label,遮罩2贴图,Mask 2 Texture,,
|
||||
inspector.feature.遮罩2通道选择.label,遮罩2通道选择,Mask 2 Channel,,
|
||||
inspector.feature.遮罩2渐变.label,遮罩2渐变(UV纵向),Mask 2 Gradient,,
|
||||
inspector.feature.遮罩2UV Wrap.label,遮罩2UV循环模式,Mask 2 UV Wrap,,
|
||||
inspector.feature.遮罩2UV来源.label,遮罩2UV来源,Mask 2 UV Source,,
|
||||
inspector.feature.遮罩2旋转.label,遮罩2旋转,Mask 2 Rotation,,
|
||||
inspector.feature.遮罩2偏移速度.label,遮罩2偏移速度,Mask 2 Offset Speed,,
|
||||
inspector.feature.遮罩3.label,遮罩3,Mask 3,,
|
||||
inspector.feature.遮罩3模式.label,遮罩3模式,Mask 3 Mode,,
|
||||
inspector.feature.遮罩3贴图.label,遮罩3贴图,Mask 3 Texture,,
|
||||
inspector.feature.遮罩3通道选择.label,遮罩3通道选择,Mask 3 Channel,,
|
||||
inspector.feature.遮罩3渐变.label,遮罩3渐变(UV横向),Mask 3 Gradient,,
|
||||
inspector.feature.遮罩3UV Wrap.label,遮罩3UV循环模式,Mask 3 UV Wrap,,
|
||||
inspector.feature.遮罩3UV来源.label,遮罩3UV来源,Mask 3 UV Source,,
|
||||
inspector.feature.遮罩3旋转.label,遮罩3旋转,Mask 3 Rotation,,
|
||||
inspector.feature.遮罩3偏移速度.label,遮罩3偏移速度,Mask 3 Offset Speed,,
|
||||
inspector.feature.扭曲.label,扭曲,Distort,,
|
||||
inspector.feature.扭曲强度值测试.label,扭曲强度值测试,Debug Distort Strength,,
|
||||
inspector.feature.整体扭曲强度.label,整体扭曲强度,Overall Distort Strength,,
|
||||
inspector.feature.扭曲强度自定义曲线.label,扭曲强度自定义曲线,Distort Strength Custom Data,,
|
||||
inspector.feature.屏幕扰动模式.label,屏幕扰动模式,Screen Distort Mode,,
|
||||
inspector.feature.屏幕扭曲强度.label,屏幕扭曲强度,Screen Distort Strength,,
|
||||
inspector.feature.关闭主材质Pass.label,关闭主材质Pass,Disable Main Material Pass,,
|
||||
inspector.feature.屏幕扭曲Alpha整体调整.label,屏幕扭曲Alpha整体调整,Screen Distort Alpha Refine,,
|
||||
inspector.feature.扭曲模式.label,扭曲模式,Distort Mode,,
|
||||
inspector.feature.扭曲贴图.label,扭曲贴图,Distort Texture,,
|
||||
inspector.feature.扭曲贴图 Wrap.label,扭曲贴图循环模式,Distort Texture Wrap,,
|
||||
inspector.feature.扭曲贴图UV来源.label,扭曲贴图UV来源,Distort Texture UV Source,,
|
||||
inspector.feature.扭曲方向强度.label,扭曲方向强度,Distort Direction Strength,,
|
||||
inspector.feature.扭曲方向强度X自定义曲线.label,扭曲方向强度X自定义曲线,Distort Direction X Custom Data,,
|
||||
inspector.feature.扭曲方向强度Y自定义曲线.label,扭曲方向强度Y自定义曲线,Distort Direction Y Custom Data,,
|
||||
inspector.feature.扭曲旋转.label,扭曲旋转,Distort Rotation,,
|
||||
inspector.feature.扭曲偏移速度.label,扭曲偏移速度,Distort Offset Speed,,
|
||||
inspector.feature.0.5为中值,双向扭曲.label,0.5为中值,双向扭曲,0.5 Is Mid Value Bidirectional Distort,,
|
||||
inspector.feature.折射率.label,折射率,Refraction IOR,,
|
||||
inspector.feature.扭曲程序噪波混合.label,扭曲程序噪波混合,Distort Program Noise Blend,,
|
||||
inspector.feature.扭曲遮罩.label,扭曲遮罩,Distort Mask,,
|
||||
inspector.feature.扭曲遮罩贴图.label,扭曲遮罩贴图,Distort Mask Texture,,
|
||||
inspector.feature.扭曲遮罩贴图 Wrap.label,扭曲遮罩贴图循环模式,Distort Mask Texture Wrap,,
|
||||
inspector.feature.扭曲遮罩图通道选择.label,扭曲遮罩图通道选择,Distort Mask Channel,,
|
||||
inspector.feature.扭曲遮罩贴图UV来源.label,扭曲遮罩贴图UV来源,Distort Mask UV Source,,
|
||||
inspector.feature.色散.label,色散,Chromatic Aberration,,
|
||||
inspector.feature.色散强度受扭曲强度影响.label,色散强度受扭曲强度影响,Chromatic Strength Follows Distort Strength,,
|
||||
inspector.feature.色散强度.label,色散强度,Chromatic Strength,,
|
||||
inspector.feature.色散强度自定义曲线.label,色散强度自定义曲线,Chromatic Strength Custom Data,,
|
||||
inspector.feature.流光(颜色相加).label,流光(颜色相加),Emission (Add Color),,
|
||||
inspector.feature.流光贴图.label,流光贴图,Emission Texture,,
|
||||
inspector.feature.流光贴图 Wrap.label,流光贴图循环模式,Emission Texture Wrap,,
|
||||
inspector.feature.流光贴图UV来源.label,流光贴图UV来源,Emission Texture UV Source,,
|
||||
inspector.feature.流光贴图X轴偏移自定义曲线.label,流光贴图X轴偏移自定义曲线,Emission Offset X Custom Data,,
|
||||
inspector.feature.流光贴图Y轴偏移自定义曲线.label,流光贴图Y轴偏移自定义曲线,Emission Offset Y Custom Data,,
|
||||
inspector.feature.流光贴图旋转.label,流光贴图旋转,Emission Texture Rotation,,
|
||||
inspector.feature.流光贴图偏移速度.label,流光贴图偏移速度,Emission Offset Speed,,
|
||||
inspector.feature.流光贴图扭曲强度.label,流光贴图扭曲强度,Emission Distortion Strength,,
|
||||
inspector.feature.流光颜色强度.label,流光颜色强度,Emission Color Intensity,,
|
||||
inspector.feature.渐变(颜色相乘).label,渐变(颜色相乘),Color Blend (Multiply),,
|
||||
inspector.feature.颜色渐变贴图.label,颜色渐变贴图,Color Blend Texture,,
|
||||
inspector.feature.颜色渐变贴图 Wrap.label,颜色渐变贴图循环模式,Color Blend Texture Wrap,,
|
||||
inspector.feature.颜色渐变贴图UV来源.label,颜色渐变贴图UV来源,Color Blend UV Source,,
|
||||
inspector.feature.颜色渐变贴图X轴偏移自定义曲线.label,颜色渐变贴图X轴偏移自定义曲线,Color Blend Offset X Custom Data,,
|
||||
inspector.feature.颜色渐变贴图Y轴偏移自定义曲线.label,颜色渐变贴图Y轴偏移自定义曲线,Color Blend Offset Y Custom Data,,
|
||||
inspector.feature.颜色渐变贴图旋转.label,颜色渐变贴图旋转,Color Blend Texture Rotation,,
|
||||
inspector.feature.颜色渐变贴图偏移速度.label,颜色渐变贴图偏移速度,Color Blend Offset Speed,,
|
||||
inspector.feature.颜色渐变扭曲强度.label,颜色渐变扭曲强度,Color Blend Distortion Strength,,
|
||||
inspector.feature.颜色渐变图Alpha作用.label,颜色渐变图Alpha作用,Color Blend Alpha Mode,,
|
||||
inspector.feature.颜色渐变图Alpha强度.label,颜色渐变图Alpha强度,Color Blend Alpha Strength,,
|
||||
inspector.feature.颜色映射(Ramp).label,颜色映射(Ramp),Ramp Color,,
|
||||
inspector.feature.Ramp来源模式.label,Ramp来源模式,Ramp Source Mode,,
|
||||
inspector.feature.颜色映射黑白图.label,颜色映射黑白图,Ramp Mask Texture,,
|
||||
inspector.feature.颜色映射黑白图 Wrap.label,颜色映射黑白图循环模式,Ramp Mask Texture Wrap,,
|
||||
inspector.feature.颜色映射UV Wrap.label,颜色映射UV循环模式,Ramp UV Wrap,,
|
||||
inspector.feature.颜色映射黑白图通道选择.label,颜色映射黑白图通道选择,Ramp Mask Channel,,
|
||||
inspector.feature.颜色映射黑白图UV来源.label,颜色映射黑白图UV来源,Ramp Mask UV Source,,
|
||||
inspector.feature.颜色映射贴图偏移速度.label,颜色映射贴图偏移速度,Ramp Texture Offset Speed,,
|
||||
inspector.feature.颜色映射贴图旋转.label,颜色映射贴图旋转,Ramp Texture Rotation,,
|
||||
inspector.feature.映射颜色.label,映射颜色,Ramp Colors,,
|
||||
inspector.feature.Ramp颜色混合模式.label,Ramp颜色混合模式,Ramp Color Blend Mode,,
|
||||
inspector.feature.颜色映射叠加颜色.label,颜色映射叠加颜色_hdr,Ramp Blend Color,,
|
||||
inspector.feature.溶解.label,溶解,Dissolve,,
|
||||
inspector.feature.溶解度黑白值测试.label,溶解度黑白值测试,Debug Dissolve Value,,
|
||||
inspector.feature.溶解贴图.label,溶解贴图,Dissolve Texture,,
|
||||
inspector.feature.溶解贴图 Wrap.label,溶解贴图循环模式,Dissolve Texture Wrap,,
|
||||
inspector.feature.溶解贴图通道选择.label,溶解贴图通道选择,Dissolve Channel,,
|
||||
inspector.feature.溶解贴图X轴偏移自定义曲线.label,溶解贴图X轴偏移自定义曲线,Dissolve Offset X Custom Data,,
|
||||
inspector.feature.溶解贴图Y轴偏移自定义曲线.label,溶解贴图Y轴偏移自定义曲线,Dissolve Offset Y Custom Data,,
|
||||
inspector.feature.溶解贴图UV来源.label,溶解贴图UV来源,Dissolve UV Source,,
|
||||
inspector.feature.溶解贴图偏移速度.label,溶解贴图偏移速度,Dissolve Offset Speed,,
|
||||
inspector.feature.溶解贴图旋转.label,溶解贴图旋转,Dissolve Texture Rotation,,
|
||||
inspector.feature.溶解程序噪波混合.label,溶解程序噪波混合,Dissolve Program Noise Blend,,
|
||||
inspector.feature.溶解值Pow.label,溶解值Pow,Dissolve Value Pow,,
|
||||
inspector.feature.溶解强度.label,溶解强度,Dissolve Strength,,
|
||||
inspector.feature.溶解强度自定义曲线.label,溶解强度自定义曲线,Dissolve Strength Custom Data,,
|
||||
inspector.feature.溶解硬软度.label,溶解硬软度,Dissolve Softness,,
|
||||
inspector.feature.溶解贴图扭曲强度.label,溶解贴图扭曲强度,Dissolve Texture Distortion Strength,,
|
||||
inspector.feature.溶解描边.label,溶解描边,Dissolve Edge,,
|
||||
inspector.feature.溶解描边颜色.label,溶解描边颜色,Dissolve Edge Color,,
|
||||
inspector.feature.描边位置.label,描边位置,Edge Position,,
|
||||
inspector.feature.描边软硬.label,描边软硬,Edge Softness,,
|
||||
inspector.feature.溶解Ramp图功能.label,溶解Ramp图功能,Dissolve Ramp,,
|
||||
inspector.feature.溶解Ramp模式.label,溶解Ramp模式,Dissolve Ramp Mode,,
|
||||
inspector.feature.溶解Ramp图.label,溶解Ramp图,Dissolve Ramp Texture,,
|
||||
inspector.feature.溶解Ramp图 Wrap.label,溶解Ramp图循环模式,Dissolve Ramp Texture Wrap,,
|
||||
inspector.feature.Ramp颜色.label,Ramp颜色,Ramp Color,,
|
||||
inspector.feature.Ramp颜色叠加.label,Ramp颜色叠加,Ramp Color Multiply,,
|
||||
inspector.feature.溶解RampUV Wrap.label,溶解RampUV循环模式,Dissolve Ramp UV Wrap,,
|
||||
inspector.feature.溶解Ramp混合模式.label,溶解Ramp混合模式,Dissolve Ramp Blend Mode,,
|
||||
inspector.feature.溶解遮罩图(过程溶解).label,溶解遮罩图(过程溶解),Dissolve Mask (Process),,
|
||||
inspector.feature.溶解遮罩模式.label,溶解遮罩模式,Dissolve Mask Mode,,
|
||||
inspector.feature.溶解遮罩图.label,溶解遮罩图,Dissolve Mask Texture,,
|
||||
inspector.feature.溶解遮罩图 Wrap.label,溶解遮罩图循环模式,Dissolve Mask Texture Wrap,,
|
||||
inspector.feature.溶解遮罩图UV来源.label,溶解遮罩图UV来源,Dissolve Mask UV Source,,
|
||||
inspector.feature.溶解遮罩图通道选择.label,溶解遮罩图通道选择,Dissolve Mask Channel,,
|
||||
inspector.feature.溶解遮罩强度.label,溶解遮罩强度,Dissolve Mask Strength,,
|
||||
inspector.feature.溶解遮罩图强度自定义曲线.label,溶解遮罩图强度自定义曲线,Dissolve Mask Strength Custom Data,,
|
||||
inspector.feature.程序化噪波.label,程序化噪波,Program Noise,,
|
||||
inspector.feature.程序化噪波测试颜色.label,程序化噪波测试颜色,Debug Program Noise,,
|
||||
inspector.feature.程序噪波UV来源.label,程序噪波UV来源,Program Noise UV Source,,
|
||||
inspector.feature.程序化噪波旋转.label,程序化噪波旋转,Program Noise Rotation,,
|
||||
inspector.feature.Perlin噪波.label,Perlin噪波,Perlin Noise,,
|
||||
inspector.feature.噪波1缩放.label,噪波1缩放,Noise 1 Scale,,
|
||||
inspector.feature.噪波1速度.label,噪波1速度,Noise 1 Speed,,
|
||||
inspector.feature.噪波1偏移.label,噪波1偏移,Noise 1 Offset,,
|
||||
inspector.feature.噪波1偏移速度.label,噪波1偏移速度,Noise 1 Offset Speed,,
|
||||
inspector.feature.噪波1偏移速度X自定义曲线.label,噪波1偏移速度X自定义曲线,Noise 1 Offset X Custom Data,,
|
||||
inspector.feature.噪波1偏移速度Y自定义曲线.label,噪波1偏移速度Y自定义曲线,Noise 1 Offset Y Custom Data,,
|
||||
inspector.feature.Voronoi噪波.label,Voronoi噪波,Voronoi Noise,,
|
||||
inspector.feature.噪波2缩放.label,噪波2缩放,Noise 2 Scale,,
|
||||
inspector.feature.噪波2速度.label,噪波2速度,Noise 2 Speed,,
|
||||
inspector.feature.噪波2偏移.label,噪波2偏移,Noise 2 Offset,,
|
||||
inspector.feature.噪波2偏移速度.label,噪波2偏移速度,Noise 2 Offset Speed,,
|
||||
inspector.feature.噪波2偏移速度X自定义曲线.label,噪波2偏移速度X自定义曲线,Noise 2 Offset X Custom Data,,
|
||||
inspector.feature.噪波2偏移速度Y自定义曲线.label,噪波2偏移速度Y自定义曲线,Noise 2 Offset Y Custom Data,,
|
||||
inspector.feature.噪波1和噪波2混合系数.label,噪波1和噪波2混合系数,Noise 1/2 Blend,,
|
||||
inspector.feature.两种程序噪波混合.label,两种程序噪波混合,Two Program Noise Blend,,
|
||||
inspector.feature.公共UV.label,公共UV,Shared UV,,
|
||||
inspector.feature.公共UV来源.label,公共UV来源,Shared UV Source,,
|
||||
inspector.feature.公共UV Tiling.label,Tilling,Shared UV Tiling,,
|
||||
inspector.feature.公共UV Offset.label,Offset,Shared UV Offset,,
|
||||
inspector.feature.公共UV偏移速度.label,偏移速度,Shared UV Offset Speed,,
|
||||
inspector.feature.旋转.label,旋转,Rotation,,
|
||||
inspector.feature.公共UV X轴偏移自定义曲线.label,偏移X自定义曲线,Shared UV Offset X Custom Data,,
|
||||
inspector.feature.公共UV Y轴偏移自定义曲线.label,偏移Y自定义曲线,Shared UV Offset Y Custom Data,,
|
||||
inspector.feature.菲涅尔.label,菲涅尔,Fresnel,,
|
||||
inspector.feature.菲涅尔测试颜色.label,菲涅尔值测试,Debug Fresnel,,
|
||||
inspector.feature.菲涅尔模式.label,菲涅尔模式,Fresnel Mode,,
|
||||
inspector.feature.菲涅尔颜色.label,菲涅尔颜色,Fresnel Color,,
|
||||
inspector.feature.菲涅尔透明乘数.label,菲涅尔透明乘数,Fresnel Alpha Multiplier,,
|
||||
inspector.feature.菲涅尔强度.label,菲涅尔强度,Fresnel Strength,,
|
||||
inspector.feature.菲涅尔位置.label,菲涅尔位置,Fresnel Position,,
|
||||
inspector.feature.菲涅尔位置自定义曲线.label,菲尼尔位置自定义曲线,Fresnel Position Custom Data,,
|
||||
inspector.feature.菲涅尔范围Pow.label,菲涅尔范围Pow,Fresnel Range Pow,,
|
||||
inspector.feature.菲涅尔硬度.label,菲涅尔硬度,Fresnel Hardness,,
|
||||
inspector.feature.翻转菲涅尔.label,翻转菲涅尔,Invert Fresnel,,
|
||||
inspector.feature.菲涅尔颜色受Alpha影响.label,菲涅尔颜色受Alpha影响,Fresnel Color Affected By Alpha,,
|
||||
inspector.feature.菲涅尔方向偏移X.label,菲涅尔方向偏移X,Fresnel Direction Offset X,,
|
||||
inspector.feature.菲涅尔方向偏移Y.label,菲涅尔方向偏移Y,Fresnel Direction Offset Y,,
|
||||
inspector.feature.菲涅尔方向偏移Z.label,菲涅尔方向偏移Z,Fresnel Direction Offset Z,,
|
||||
inspector.feature.顶点偏移.label,顶点偏移,Vertex Offset,,
|
||||
inspector.feature.顶点偏移方向测试.label,顶点偏移方向测试,Debug Vertex Offset Direction,,
|
||||
inspector.feature.顶点偏移贴图.label,顶点偏移贴图,Vertex Offset Texture,,
|
||||
inspector.feature.顶点偏移贴图 Wrap.label,顶点偏移贴图循环模式,Vertex Offset Texture Wrap,,
|
||||
inspector.feature.顶点偏移贴图UV来源.label,顶点偏移贴图UV来源,Vertex Offset UV Source,,
|
||||
inspector.feature.顶点扰动X轴偏移自定义曲线.label,顶点扰动X轴偏移自定义曲线,Vertex Offset X Custom Data,,
|
||||
inspector.feature.顶点扰动Y轴偏移自定义曲线.label,顶点扰动Y轴偏移自定义曲线,Vertex Offset Y Custom Data,,
|
||||
inspector.feature.顶点偏移动画.label,顶点偏移动画,Vertex Offset Animation,,
|
||||
inspector.feature.顶点偏移强度.label,顶点偏移强度,Vertex Offset Strength,,
|
||||
inspector.feature.顶点扰动强度自定义曲线.label,顶点扰动强度自定义曲线,Vertex Offset Strength Custom Data,,
|
||||
inspector.feature.顶点偏移从零开始.label,顶点偏移从零开始,Vertex Offset Starts From Zero,,
|
||||
inspector.feature.顶点偏移使用法线方向.label,顶点偏移使用法线方向,Use Normal Direction,,
|
||||
inspector.feature.顶点偏移本地方向X.label,顶点偏移本地方向X,Vertex Offset Local Direction X,,
|
||||
inspector.feature.顶点偏移本地方向Y.label,顶点偏移本地方向Y,Vertex Offset Local Direction Y,,
|
||||
inspector.feature.顶点偏移本地方向Z.label,顶点偏移本地方向Z,Vertex Offset Local Direction Z,,
|
||||
inspector.feature.顶点偏移遮罩.label,顶点偏移遮罩,Vertex Offset Mask,,
|
||||
inspector.feature.顶点偏移遮罩图.label,顶点偏移遮罩图,Vertex Offset Mask Texture,,
|
||||
inspector.feature.顶点偏移遮罩图 Wrap.label,顶点偏移遮罩图循环模式,Vertex Offset Mask Texture Wrap,,
|
||||
inspector.feature.顶点偏移遮罩图UV来源.label,顶点偏移遮罩图UV来源,Vertex Offset Mask UV Source,,
|
||||
inspector.feature.顶点扰动遮罩X轴偏移自定义曲线.label,顶点扰动遮罩X轴偏移自定义曲线,Vertex Offset Mask X Custom Data,,
|
||||
inspector.feature.顶点扰动遮罩Y轴偏移自定义曲线.label,顶点扰动遮罩Y轴偏移自定义曲线,Vertex Offset Mask Y Custom Data,,
|
||||
inspector.feature.顶点偏移遮罩动画.label,顶点偏移遮罩动画,Vertex Offset Mask Animation,,
|
||||
inspector.feature.顶点偏移遮罩强度.label,顶点偏移遮罩强度,Vertex Offset Mask Strength,,
|
||||
inspector.feature.深度描边.label,深度描边,Depth Outline,,
|
||||
inspector.feature.深度描边颜色.label,深度描边颜色,Depth Outline Color,,
|
||||
inspector.feature.深度描边距离.label,深度描边距离,Depth Outline Distance,,
|
||||
inspector.feature.深度贴花.label,深度贴花,Depth Decal,,
|
||||
inspector.feature.遮蔽视差.label,遮蔽视差,Parallax,,
|
||||
inspector.feature.视差贴图.label,视差贴图,Parallax Texture,,
|
||||
inspector.feature.视差贴图 Wrap.label,视差贴图循环模式,Parallax Texture Wrap,,
|
||||
inspector.feature.视差.label,视差,Parallax,,
|
||||
inspector.feature.遮蔽视差最小层数.label,遮蔽视差最小层数,Parallax Min Layers,,
|
||||
inspector.feature.遮蔽视差最大层数.label,遮蔽视差最大层数,Parallax Max Layers,,
|
||||
inspector.feature.模板视差.label,模板视差,Portal,,
|
||||
inspector.feature.模板视差蒙版.label,模板视差蒙版,Portal Mask,,
|
||||
inspector.feature.序列帧融帧(丝滑).label,序列帧融帧(丝滑),Flipbook Blending,,
|
||||
inspector.feature.VAT顶点动画图.label,VAT顶点动画图,VAT Vertex Animation Texture,,
|
||||
inspector.feature.VAT模式.label,VAT模式,VAT Mode,,
|
||||
inspector.feature.Houdini VAT Sub Mode.label,Houdini VAT Sub Mode,Houdini VAT Sub Mode,,
|
||||
inspector.feature.VAT Frame CustomData.label,VAT Frame CustomData,VAT Frame CustomData,,
|
||||
inspector.feature.Playback.label,Playback,Playback,,
|
||||
inspector.feature.Auto Playback.label,Auto Playback,Auto Playback,,
|
||||
inspector.feature.Display Frame.label,Display Frame,Display Frame,,
|
||||
inspector.feature.Game Time at First Frame.label,Game Time at First Frame,Game Time at First Frame,,
|
||||
inspector.feature.Playback Speed.label,Playback Speed,Playback Speed,,
|
||||
inspector.feature.Houdini FPS.label,Houdini FPS,Houdini FPS,,
|
||||
inspector.feature.Interframe Interpolation.label,Interframe Interpolation,Interframe Interpolation,,
|
||||
inspector.feature.Animate First Frame.label,Animate First Frame,Animate First Frame,,
|
||||
inspector.feature.Frame Count.label,Frame Count,Frame Count,,
|
||||
inspector.feature.Bounds Metadata.label,Bounds Metadata,Bounds Metadata,,
|
||||
inspector.feature.Bound Min X.label,Bound Min X,Bound Min X,,
|
||||
inspector.feature.Bound Min Y.label,Bound Min Y,Bound Min Y,,
|
||||
inspector.feature.Bound Min Z.label,Bound Min Z,Bound Min Z,,
|
||||
inspector.feature.Bound Max X.label,Bound Max X,Bound Max X,,
|
||||
inspector.feature.Bound Max Y.label,Bound Max Y,Bound Max Y,,
|
||||
inspector.feature.Bound Max Z.label,Bound Max Z,Bound Max Z,,
|
||||
inspector.feature.Textures.label,Textures,Textures,,
|
||||
inspector.feature.Position Texture.label,Position Texture,Position Texture,,
|
||||
inspector.feature.Position Texture 2.label,Position Texture 2,Position Texture 2,,
|
||||
inspector.feature.Rotation Texture.label,Rotation Texture,Rotation Texture,,
|
||||
inspector.feature.Color Texture.label,Color Texture,Color Texture,,
|
||||
inspector.feature.Lookup Table.label,Lookup Table,Lookup Table,,
|
||||
inspector.feature.Scale.label,Scale,Scale,,
|
||||
inspector.feature.Global Piece Scale Multiplier.label,Global Piece Scale Multiplier,Global Piece Scale Multiplier,,
|
||||
inspector.feature.Piece Scales in Position Alpha.label,Piece Scales in Position Alpha,Piece Scales in Position Alpha,,
|
||||
inspector.feature.Particle Sprite.label,Particle Sprite,Particle Sprite,,
|
||||
inspector.feature.Width Base Scale.label,Width Base Scale,Width Base Scale,,
|
||||
inspector.feature.Height Base Scale.label,Height Base Scale,Height Base Scale,,
|
||||
inspector.feature.Hide Overlapping Origin.label,Hide Overlapping Origin,Hide Overlapping Origin,,
|
||||
inspector.feature.Origin Effective Radius.label,Origin Effective Radius,Origin Effective Radius,,
|
||||
inspector.feature.Particles Can Spin.label,Particles Can Spin,Particles Can Spin,,
|
||||
inspector.feature.Compute Spin from Heading.label,Compute Spin from Heading,Compute Spin from Heading,,
|
||||
inspector.feature.Particle Spin Phase.label,Particle Spin Phase,Particle Spin Phase,,
|
||||
inspector.feature.Scale by Velocity Amount.label,Scale by Velocity Amount,Scale by Velocity Amount,,
|
||||
inspector.feature.Particle Texture U Scale.label,Particle Texture U Scale,Particle Texture U Scale,,
|
||||
inspector.feature.Particle Texture V Scale.label,Particle Texture V Scale,Particle Texture V Scale,,
|
||||
inspector.feature.Flags.label,Flags,Flags,,
|
||||
inspector.feature.Positions Require Two Textures.label,Positions Require Two Textures,Positions Require Two Textures,,
|
||||
inspector.feature.Use Compressed Normals (no rotTex).label,Use Compressed Normals (no rotTex),Use Compressed Normals (no rotTex),,
|
||||
inspector.feature.Load Color Texture.label,Load Color Texture,Load Color Texture,,
|
||||
inspector.feature.Load Lookup Table.label,Load Lookup Table,Load Lookup Table,,
|
||||
inspector.feature.VAT texture.label,VAT texture,VAT Texture,,
|
||||
inspector.feature.ImportScale.label,ImportScale,Import Scale,,
|
||||
inspector.feature.TyFlow VAT Sub Mode.label,TyFlow VAT Sub Mode,TyFlow VAT Sub Mode,,
|
||||
inspector.feature.Deforming skin.label,Deforming skin,Deforming Skin,,
|
||||
inspector.feature.Skin bone count.label,Skin bone count,Skin Bone Count,,
|
||||
inspector.feature.RGBA encoded.label,RGBA encoded,RGBA Encoded,,
|
||||
inspector.feature.RGBA half.label,RGBA half,RGBA Half,,
|
||||
inspector.feature.Gamma correction.label,Gamma correction,Gamma Correction,,
|
||||
inspector.feature.VAT includes normals.label,VAT includes normals,VAT Includes Normals,,
|
||||
inspector.feature.Affects shadows.label,Affects shadows,Affects Shadows,,
|
||||
inspector.feature.Frame.label,Frame,Frame,,
|
||||
inspector.feature.Frames.label,Frames,Frames,,
|
||||
inspector.feature.Frame interpolation.label,Frame interpolation,Frame Interpolation,,
|
||||
inspector.feature.Loop.label,Loop,Loop,,
|
||||
inspector.feature.Interpolate loop.label,Interpolate loop,Interpolate Loop,,
|
||||
inspector.feature.Autoplay.label,Autoplay,Autoplay,,
|
||||
inspector.feature.AutoplaySpeed.label,AutoplaySpeed,Autoplay Speed,,
|
||||
inspector.toolbar.ping.label,,,,
|
||||
inspector.toolbar.ping.tip,跳到当前材质,Ping current material,,
|
||||
inspector.toolbar.cleanUnusedTextures.label,,,,
|
||||
inspector.toolbar.cleanUnusedTextures.tip,清除没有使用的贴图,Clean unused textures,,
|
||||
inspector.toolbar.cleanUnusedTextures.undo,清除没有使用的贴图,Clean unused textures,,
|
||||
inspector.toolbar.cleanUnusedTextures.log,清理 {0} 的无效贴图属性: {1},Cleaned unused texture property '{1}' on material '{0}',,
|
||||
inspector.toolbar.copy.label,C,C,复制材质属性,Copy material properties
|
||||
inspector.toolbar.paste.label,V,V,粘贴材质属性,Paste material properties
|
||||
inspector.toolbar.paste.undo,粘贴材质属性,Paste material properties,,
|
||||
inspector.toolbar.tierFormat.label,等级: {0},Tier: {0},,
|
||||
inspector.toolbar.tierMixed.label,等级: Mixed,Tier: Mixed,,
|
||||
inspector.toolbar.tierLow.label,低,Low,,
|
||||
inspector.toolbar.tierMedium.label,中,Medium,,
|
||||
inspector.toolbar.tierHigh.label,高,High,,
|
||||
inspector.toolbar.tierUltra.label,顶配,Ultra,,
|
||||
inspector.toolbar.tier.tip,NBShader分级,NBShader feature tier,,
|
||||
inspector.toolbar.setTier.undo,设置 NBShader 分级,Set NBShader Feature Tier,,
|
||||
inspector.toolbar.specialReset.label,R,R,特殊重置功能,Special reset tools
|
||||
inspector.toolbar.resetAll.label,重置所有,Reset All,,
|
||||
inspector.toolbar.resetAll.undo,重置所有特殊功能,Reset All Special Tools,,
|
||||
inspector.toolbar.resetDisabledFeatureChildren.label,重置关闭功能子属性,Reset Disabled Feature Children,,
|
||||
inspector.toolbar.resetDisabledFeatureChildren.undo,重置关闭功能子属性,Reset Disabled Feature Children,,
|
||||
inspector.toolbar.resetSpecialUV.label,重置特殊UV通道,Reset Special UV Channel,,
|
||||
inspector.toolbar.resetSpecialUV.undo,重置特殊UV通道,Reset Special UV Channel,,
|
||||
inspector.toolbar.resetTwirl.label,重置旋转扭曲,Reset Twirl,,
|
||||
inspector.toolbar.resetTwirl.undo,重置旋转扭曲,Reset Twirl,,
|
||||
inspector.toolbar.resetPolar.label,重置极坐标,Reset Polar Coordinates,,
|
||||
inspector.toolbar.resetPolar.undo,重置极坐标,Reset Polar Coordinates,,
|
||||
inspector.toolbar.collapseAll.label,,,,
|
||||
inspector.toolbar.collapseAll.tip,折叠所有控件,Collapse all controls,,
|
||||
inspector.toolbar.collapseAll.undo,折叠所有控件,Collapse all controls,,
|
||||
inspector.toolbar.help.label,,,,
|
||||
inspector.toolbar.help.tip,说明文档,Documentation,,
|
||||
inspector.toolbar.settings.tip,NBShader设置,NBShader settings,,
|
||||
inspector.projectSettings.generalSection,基础设置,General Settings,,
|
||||
inspector.projectSettings.defaultLanguage,默认语言,Default Language,,
|
||||
inspector.projectSettings.languageMode.option.0,跟随编辑器,Follow Editor,,
|
||||
inspector.projectSettings.languageMode.option.1,中文,Chinese,,
|
||||
inspector.projectSettings.languageMode.option.2,英文,English,,
|
||||
inspector.featureLevel.providerLabel,NBShader 分级,NBShader Feature Levels,,
|
||||
inspector.featureLevel.help.message,配置 NBShader Catalog Keyword 和 Pass 的分级白名单、Unity Quality 绑定和运行时资源导出。构建脚本通过 NBShaderFeatureLevelEditorAPI.OverrideBuildStripExplicitTier 选择打包剔除等级。Catalog 外 Feature 会被忽略。,Configure NBShader managed Catalog keywords and shader passes per tier bind Unity Quality levels and export runtime settings. Build scripts select the build stripping tier through NBShaderFeatureLevelEditorAPI.OverrideBuildStripExplicitTier. Catalog-external features are ignored.,,
|
||||
inspector.featureLevel.undo.changeKeyword,修改 NBShader Feature Keyword,Change NBShader Feature Keyword,,
|
||||
inspector.featureLevel.undo.resetTierKeywords,重置 NBShader 等级 Keyword,Reset NBShader Tier Keywords,,
|
||||
inspector.featureLevel.undo.resetQualityMapping,重置 NBShader Quality 映射,Reset NBShader Quality Mapping,,
|
||||
inspector.featureLevel.undo.changeQualityBinding,修改 NBShader Quality 绑定,Change NBShader Quality Binding,,
|
||||
inspector.featureLevel.table.title,分级功能表,Feature Level Matrix,,
|
||||
inspector.featureLevel.column.feature.label,配置 / Feature,Config / Feature,等级设置和受管理的 Catalog Feature,Tier settings and managed Catalog features
|
||||
inspector.featureLevel.column.description.label,Raw Keyword / 说明,Raw Keyword / Note,原始 Shader Keyword 或配置说明,Raw shader keyword or configuration note
|
||||
inspector.featureLevel.row.quality.label,Quality 绑定,Quality Binding,每个 Unity Quality Level 只能属于一个 NBShader 等级,Each Unity Quality Level belongs to exactly one NBShader tier
|
||||
inspector.featureLevel.desc.quality,Unity Quality Level,Unity Quality Level,,
|
||||
inspector.featureLevel.quality.menuTip,点击可把 Unity Quality Level 移动到这个 NBShader 等级,Click to move Unity Quality levels to this NBShader tier,,
|
||||
inspector.featureLevel.quality.noLevels,没有 Quality Level,No Quality Levels,,
|
||||
inspector.featureLevel.quality.none,—,—,,
|
||||
inspector.featureLevel.keyword.tooltip,Raw Keyword:{0}。该等级未勾选时会剔除使用此 Catalog Keyword 的 Variant。,Raw keyword: {0}. Unchecked in a tier strips variants using this Catalog keyword.,,
|
||||
inspector.featureLevel.resetTierKeywords.button,重置等级 Keywords,Reset Tier Keywords,将 Feature 表格重置为内置默认值,Reset the keyword matrix to the built-in defaults
|
||||
inspector.featureLevel.resetQualityMapping.button,重置 Quality 映射,Reset Quality Mapping,按默认 Quality 索引规则重置 Unity Quality 绑定,Reset Unity Quality bindings using the default quality-index rule
|
||||
inspector.featureRuntimeSettings.updateFromProjectSettings.button,根据当前ProjectSetting更新配置,Update From Current Project Settings,将当前 Project Settings 数据写入这个 RuntimeSettings 配置资源,Write the current Project Settings data into this RuntimeSettings asset
|
||||
inspector.featureRuntimeSettings.updateFromProjectSettings.successTitle,运行时配置已更新,Runtime Settings Updated,,
|
||||
inspector.featureRuntimeSettings.updateFromProjectSettings.successMessage,当前 NBShader Project Settings 数据已写入这个 RuntimeSettings 配置资源,The current NBShader Project Settings data was written to this RuntimeSettings asset,,
|
||||
inspector.featureLevel.openVariantCollectionBuilder.button,打开变体集合生成器,Open Variant Collection Builder,打开 NBShader 变体集合生成器,Open the NBShader variant collection builder
|
||||
inspector.featureLevel.applyCurrentQualityTierToLoadedMaterials.button,应用当前 Quality 等级到已加载材质,Apply Current Quality Tier To Loaded Materials,将当前 Unity Quality 映射到的 NBShader 等级应用到所有已加载的 NBShader2 材质,Apply the tier bound to the current Unity Quality Level to all loaded NBShader2 materials
|
||||
inspector.featureLevel.applyCurrentQualityTierToProjectMaterials.button,应用当前 Quality 等级到项目材质,Apply Current Quality Tier To Project Materials,扫描 Assets 并将当前 Unity Quality 映射到的 NBShader 等级应用到 NBShader2 材质资源,Scan Assets and apply the tier bound to the current Unity Quality Level to NBShader2 material assets
|
||||
inspector.featureLevel.keyword.NB_DEBUG_DISSOLVE.label,调试溶解,Debug Dissolve,,
|
||||
inspector.featureLevel.keyword.NB_DEBUG_DISTORT.label,调试扭曲,Debug Distort,,
|
||||
inspector.featureLevel.keyword.NB_DEBUG_FRESNEL.label,调试菲涅尔,Debug Fresnel,,
|
||||
inspector.featureLevel.keyword.NB_DEBUG_MASK.label,调试遮罩,Debug Mask,,
|
||||
inspector.featureLevel.keyword.NB_DEBUG_PNOISE.label,调试程序噪声,Debug Program Noise,,
|
||||
inspector.featureLevel.keyword.NB_DEBUG_VERTEX_OFFSET.label,调试顶点偏移,Debug Vertex Offset,,
|
||||
inspector.featureLevel.keyword.VFX_SIX_WAY_ABSORPTION.label,六向光吸收,Six Way Absorption,,
|
||||
inspector.featureLevel.keyword._ALPHAMODULATE_ON.label,Alpha 调制,Alpha Modulate,,
|
||||
inspector.featureLevel.keyword._ALPHAPREMULTIPLY_ON.label,预乘 Alpha,Premultiply Alpha,,
|
||||
inspector.featureLevel.keyword._ALPHATEST_ON.label,Alpha 裁剪,Alpha Test,,
|
||||
inspector.featureLevel.keyword._COLORMAPBLEND.label,颜色图混合,Color Map Blend,,
|
||||
inspector.featureLevel.keyword._COLOR_RAMP.label,颜色渐变,Color Ramp,,
|
||||
inspector.featureLevel.keyword._DEPTH_DECAL.label,深度贴花,Depth Decal,,
|
||||
inspector.featureLevel.keyword._DISSOLVE.label,溶解,Dissolve,,
|
||||
inspector.featureLevel.keyword._DISTORT_REFRACTION.label,折射扭曲,Distort Refraction,,
|
||||
inspector.featureLevel.keyword._EMISSION.label,自发光,Emission,,
|
||||
inspector.featureLevel.keyword._FLIPBOOKBLENDING_ON.label,序列帧混合,Flipbook Blending,,
|
||||
inspector.featureLevel.keyword._FX_LIGHT_MODE_BLINN_PHONG.label,Blinn-Phong 光照,Blinn-Phong Lighting,,
|
||||
inspector.featureLevel.keyword._FX_LIGHT_MODE_HALF_LAMBERT.label,Half Lambert 光照,Half Lambert Lighting,,
|
||||
inspector.featureLevel.keyword._FX_LIGHT_MODE_PBR.label,PBR 光照,PBR Lighting,,
|
||||
inspector.featureLevel.keyword._FX_LIGHT_MODE_SIX_WAY.label,六向光照,Six Way Lighting,,
|
||||
inspector.featureLevel.keyword._FX_LIGHT_MODE_UNLIT.label,无光照,Unlit Lighting,,
|
||||
inspector.featureLevel.keyword._HOUDINI_VAT_DYNAMIC_REMESH.label,Houdini VAT 动态重网格,Houdini VAT Dynamic Remesh,,
|
||||
inspector.featureLevel.keyword._HOUDINI_VAT_PARTICLE_SPRITE.label,Houdini VAT 粒子精灵,Houdini VAT Particle Sprite,,
|
||||
inspector.featureLevel.keyword._HOUDINI_VAT_RIGIDBODY.label,Houdini VAT 刚体,Houdini VAT Rigid Body,,
|
||||
inspector.featureLevel.keyword._HOUDINI_VAT_SOFTBODY.label,Houdini VAT 软体,Houdini VAT Soft Body,,
|
||||
inspector.featureLevel.keyword._MASKMAP_ON.label,遮罩贴图,Mask Map,,
|
||||
inspector.featureLevel.keyword._MATCAP.label,MatCap,MatCap,,
|
||||
inspector.featureLevel.keyword._NOISEMAP.label,噪声贴图,Noise Map,,
|
||||
inspector.featureLevel.keyword._NOISEMAP_NORMALIZEED.label,噪声归一化,Noise Normalize,,
|
||||
inspector.featureLevel.keyword._NORMALMAP.label,法线贴图,Normal Map,,
|
||||
inspector.featureLevel.keyword._OVERRIDE_Z.label,Override Z,Override Z,,
|
||||
inspector.featureLevel.keyword._PARALLAX_MAPPING.label,视差映射,Parallax Mapping,,
|
||||
inspector.featureLevel.keyword._PARCUSTOMDATA_ON.label,粒子自定义数据,Particle Custom Data,,
|
||||
inspector.featureLevel.keyword._PROGRAM_NOISE.label,程序噪声,Program Noise,,
|
||||
inspector.featureLevel.keyword._SCREEN_DISTORT_MODE.label,屏幕扭曲,Screen Distort,,
|
||||
inspector.featureLevel.keyword._SCRIPTABLETIME.label,可编程时间,Scriptable Time,,
|
||||
inspector.featureLevel.keyword._SHARED_UV.label,共享 UV,Shared UV,,
|
||||
inspector.featureLevel.keyword._SOFTPARTICLES_ON.label,软粒子,Soft Particles,,
|
||||
inspector.featureLevel.keyword._SPECULAR_COLOR.label,高光颜色,Specular Color,,
|
||||
inspector.featureLevel.keyword._STENCIL_WITHOUT_PLAYER.label,无 Player 模板,Stencil Without Player,,
|
||||
inspector.featureLevel.keyword._TYFLOW_VAT_ABSOLUTE.label,TyFlow VAT 绝对模式,TyFlow VAT Absolute,,
|
||||
inspector.featureLevel.keyword._TYFLOW_VAT_RELATIVE.label,TyFlow VAT 相对模式,TyFlow VAT Relative,,
|
||||
inspector.featureLevel.keyword._TYFLOW_VAT_SKIN_PR.label,TyFlow VAT 皮肤 PR,TyFlow VAT Skin PR,,
|
||||
inspector.featureLevel.keyword._TYFLOW_VAT_SKIN_PRSAVE.label,TyFlow VAT 皮肤 PR 保存,TyFlow VAT Skin PR Save,,
|
||||
inspector.featureLevel.keyword._TYFLOW_VAT_SKIN_PRSXYZ.label,TyFlow VAT 皮肤 PRSXYZ,TyFlow VAT Skin PRSXYZ,,
|
||||
inspector.featureLevel.keyword._TYFLOW_VAT_SKIN_R.label,TyFlow VAT 皮肤 R,TyFlow VAT Skin R,,
|
||||
inspector.featureLevel.keyword._UNSCALETIME.label,非缩放时间,Unscaled Time,,
|
||||
inspector.featureLevel.keyword._VAT.label,VAT,VAT,,
|
||||
inspector.featureLevel.keyword._VAT_HOUDINI.label,Houdini VAT,Houdini VAT,,
|
||||
inspector.featureLevel.keyword._VAT_TYFLOW.label,TyFlow VAT,TyFlow VAT,,
|
||||
inspector.featureLevel.group.tooltip,点击折叠按钮显示或隐藏子级 Row,Click the foldout to show or hide child rows,,
|
||||
inspector.featureLevel.desc.group,分组,Group,,
|
||||
inspector.featureLevel.group.mode.label,模式设置,Mode,跟随材质面板的模式设置顺序,Matches the material inspector Mode order
|
||||
inspector.featureLevel.group.transparent.label,透明模式,Transparent Mode,透明模式相关 Keyword,Transparent mode related keywords
|
||||
inspector.featureLevel.group.time.label,时间模式,Time Mode,时间来源相关 Keyword,Time source related keywords
|
||||
inspector.featureLevel.group.base.label,基本全局功能,Base,跟随材质面板的基本全局功能顺序,Matches the material inspector Base order
|
||||
inspector.featureLevel.group.particle.label,粒子数据,Particle Data,粒子自定义数据相关 Keyword,Particle custom data related keywords
|
||||
inspector.featureLevel.group.light.label,光照功能,Lighting,跟随材质面板的光照功能顺序,Matches the material inspector Lighting order
|
||||
inspector.featureLevel.group.lightMode.label,光照模式,Light Mode,光照模式 Keyword,Light mode keywords
|
||||
inspector.featureLevel.group.feature.label,特别功能,Feature,跟随材质面板的特别功能 Toggle 顺序,Matches the material inspector Feature toggle order
|
||||
inspector.featureLevel.group.fresnel.label,菲涅尔,Fresnel,菲涅尔相关 Keyword,Fresnel related keywords
|
||||
inspector.featureLevel.group.vertexOffset.label,顶点偏移,Vertex Offset,顶点偏移相关 Keyword,Vertex offset related keywords
|
||||
inspector.featureLevel.group.depth.label,深度功能,Depth,深度相关 Keyword,Depth related keywords
|
||||
inspector.featureLevel.group.vatMode.label,VAT 模式,VAT Mode,VAT 主模式 Keyword,VAT main mode keywords
|
||||
inspector.featureLevel.group.houdiniVat.label,Houdini VAT,Houdini VAT,Houdini VAT 子模式 Keyword,Houdini VAT sub-mode keywords
|
||||
inspector.featureLevel.group.tyflowVat.label,TyFlow VAT,TyFlow VAT,TyFlow VAT 子模式 Keyword,TyFlow VAT sub-mode keywords
|
||||
inspector.featureLevel.column.cost.label,性能消耗,Performance Cost,估算启用该功能后的 Shader 性能消耗,Estimated shader cost when this feature is enabled
|
||||
inspector.featureLevel.column.effect.label,功能说明,Feature Effect,简要说明该 Keyword 控制的功能,Short description of what this keyword enables
|
||||
inspector.featureLevel.cost.low,低,Low,,
|
||||
inspector.featureLevel.cost.medium,中,Medium,,
|
||||
inspector.featureLevel.cost.high,高,High,,
|
||||
inspector.featureLevel.cost.ultra,超高,Ultra,,
|
||||
inspector.featureLevel.cost.tooltip,该 Keyword 估算的 Shader 性能消耗,Estimated shader performance cost for this keyword,,
|
||||
inspector.featureLevel.desc.none,—,—,,
|
||||
inspector.featureLevel.desc.config,配置,Config,,
|
||||
inspector.featureLevel.effect.quality.label,绑定 Unity Quality 到 NBShader 等级,Bind Unity Quality Levels to NBShader tiers,每个 Unity Quality Level 只能属于一个 NBShader 等级,Each Unity Quality Level belongs to exactly one NBShader tier
|
||||
inspector.featureLevel.keyword._CHROMATIC_ABERRATION.label,色散,Chromatic Aberration,,
|
||||
inspector.featureLevel.keyword._COLOR_RAMP_MAP.label,颜色渐变贴图,Color Ramp Map,,
|
||||
inspector.featureLevel.keyword._DEPTH_OUTLINE.label,深度描边,Depth Outline,,
|
||||
inspector.featureLevel.keyword._DISSOLVE_MASK.label,溶解遮罩,Dissolve Mask,,
|
||||
inspector.featureLevel.keyword._DISSOLVE_RAMP.label,溶解 Ramp,Dissolve Ramp,,
|
||||
inspector.featureLevel.keyword._DISSOLVE_RAMP_MAP.label,溶解 Ramp 图,Dissolve Ramp Map,,
|
||||
inspector.featureLevel.keyword._DISTANCE_FADE.label,距离淡化,Distance Fade,,
|
||||
inspector.featureLevel.keyword._FRESNEL.label,菲涅尔,Fresnel,,
|
||||
inspector.featureLevel.keyword._MASKMAP2_ON.label,遮罩贴图 2,Mask Map 2,,
|
||||
inspector.featureLevel.keyword._MASKMAP3_ON.label,遮罩贴图 3,Mask Map 3,,
|
||||
inspector.featureLevel.keyword._NOISE_MASKMAP.label,扭曲遮罩,Noise Mask Map,,
|
||||
inspector.featureLevel.keyword._PROGRAM_NOISE_SIMPLE.label,程序噪声 Perlin,Program Noise Simple,,
|
||||
inspector.featureLevel.keyword._PROGRAM_NOISE_VORONOI.label,程序噪声 Voronoi,Program Noise Voronoi,,
|
||||
inspector.featureLevel.keyword._VERTEX_OFFSET.label,顶点偏移,Vertex Offset,,
|
||||
inspector.featureLevel.keyword._VERTEX_OFFSET_MASKMAP.label,顶点偏移遮罩,Vertex Offset Mask Map,,
|
||||
inspector.featureLevel.keyword.NB_DEBUG_DISSOLVE.effect,显示溶解数值方便调试,Shows dissolve values for debugging,,
|
||||
inspector.featureLevel.keyword.NB_DEBUG_DISTORT.effect,显示扭曲强度方便调试,Shows distortion strength for debugging,,
|
||||
inspector.featureLevel.keyword.NB_DEBUG_FRESNEL.effect,显示菲涅尔数值方便调试,Shows fresnel values for debugging,,
|
||||
inspector.featureLevel.keyword.NB_DEBUG_MASK.effect,显示遮罩数值方便调试,Shows mask values for debugging,,
|
||||
inspector.featureLevel.keyword.NB_DEBUG_PNOISE.effect,显示程序噪声数值方便调试,Shows procedural noise values for debugging,,
|
||||
inspector.featureLevel.keyword.NB_DEBUG_VERTEX_OFFSET.effect,显示顶点偏移数值方便调试,Shows vertex offset values for debugging,,
|
||||
inspector.featureLevel.keyword.VFX_SIX_WAY_ABSORPTION.effect,为六向光照增加吸收效果,Adds absorption response for six way lighting,,
|
||||
inspector.featureLevel.keyword._ALPHAMODULATE_ON.effect,使用 Alpha 调制透明混合,Uses alpha modulation transparent blending,,
|
||||
inspector.featureLevel.keyword._ALPHAPREMULTIPLY_ON.effect,使用预乘 Alpha 透明混合,Uses premultiplied alpha blending,,
|
||||
inspector.featureLevel.keyword._ALPHATEST_ON.effect,按 Alpha 阈值裁剪像素,Cuts pixels by alpha threshold,,
|
||||
inspector.featureLevel.keyword._CHROMATIC_ABERRATION.effect,分离颜色通道产生色散,Splits color channels for distortion fringe,,
|
||||
inspector.featureLevel.keyword._COLORMAPBLEND.effect,混合额外的颜色渐变贴图,Blends an extra color gradient texture,,
|
||||
inspector.featureLevel.keyword._COLOR_RAMP.effect,使用 Ramp 重新映射颜色,Remaps color through a ramp,,
|
||||
inspector.featureLevel.keyword._COLOR_RAMP_MAP.effect,使用贴图作为颜色 Ramp 来源,Uses a texture as the color ramp source,,
|
||||
inspector.featureLevel.keyword._DEPTH_DECAL.effect,使用场景深度投射贴花效果,Projects decal effect using scene depth,,
|
||||
inspector.featureLevel.keyword._DEPTH_OUTLINE.effect,使用深度差生成描边效果,Creates outline effect using depth difference,,
|
||||
inspector.featureLevel.keyword._OVERRIDE_Z.effect,写入固定相机眼深度到 SV_Depth,Writes a fixed camera eye depth through SV_Depth,,
|
||||
inspector.featureLevel.keyword._DISSOLVE.effect,按噪声或贴图裁剪并混合溶解,Clips and blends pixels for dissolve,,
|
||||
inspector.featureLevel.keyword._DISSOLVE_MASK.effect,使用遮罩控制过程溶解范围,Uses a process mask for dissolve,,
|
||||
inspector.featureLevel.keyword._DISSOLVE_RAMP.effect,为溶解边缘增加 Ramp 着色,Adds ramp coloring to dissolve edges,,
|
||||
inspector.featureLevel.keyword._DISSOLVE_RAMP_MAP.effect,使用贴图控制溶解 Ramp 颜色,Uses a texture for dissolve ramp color,,
|
||||
inspector.featureLevel.keyword._DISTANCE_FADE.effect,根据相机距离淡化材质,Fades material by camera distance,,
|
||||
inspector.featureLevel.keyword._DISTORT_REFRACTION.effect,使用折射方式计算扭曲,Uses refraction style distortion,,
|
||||
inspector.featureLevel.keyword._EMISSION.effect,增加自发光颜色或贴图贡献,Adds emissive color or texture contribution,,
|
||||
inspector.featureLevel.keyword._FLIPBOOKBLENDING_ON.effect,在序列帧之间进行平滑混合,Blends between flipbook frames,,
|
||||
inspector.featureLevel.keyword._FRESNEL.effect,增加基于视角的边缘光,Adds view angle based rim lighting,,
|
||||
inspector.featureLevel.keyword._FX_LIGHT_MODE_BLINN_PHONG.effect,使用 Blinn Phong 光照模型,Uses Blinn Phong lighting,,
|
||||
inspector.featureLevel.keyword._FX_LIGHT_MODE_HALF_LAMBERT.effect,使用 Half Lambert 光照模型,Uses half Lambert lighting,,
|
||||
inspector.featureLevel.keyword._FX_LIGHT_MODE_PBR.effect,使用 PBR 光照计算,Uses PBR lighting calculations,,
|
||||
inspector.featureLevel.keyword._FX_LIGHT_MODE_SIX_WAY.effect,使用六向光照数据,Uses six way lighting data,,
|
||||
inspector.featureLevel.keyword._FX_LIGHT_MODE_UNLIT.effect,使用无光照渲染,Uses unlit shading,,
|
||||
inspector.featureLevel.keyword._HOUDINI_VAT_DYNAMIC_REMESH.effect,使用 Houdini 动态重网格 VAT 类型,Uses Houdini dynamic remesh VAT mode,,
|
||||
inspector.featureLevel.keyword._HOUDINI_VAT_PARTICLE_SPRITE.effect,使用 Houdini 粒子精灵 VAT 类型,Uses Houdini particle sprite VAT mode,,
|
||||
inspector.featureLevel.keyword._HOUDINI_VAT_RIGIDBODY.effect,使用 Houdini 刚体 VAT 类型,Uses Houdini rigid body VAT mode,,
|
||||
inspector.featureLevel.keyword._HOUDINI_VAT_SOFTBODY.effect,使用 Houdini 软体 VAT 类型,Uses Houdini soft body VAT mode,,
|
||||
inspector.featureLevel.keyword._MASKMAP_ON.effect,启用第一张遮罩贴图,Uses the first mask map,,
|
||||
inspector.featureLevel.keyword._MASKMAP2_ON.effect,启用第二张遮罩贴图,Uses the second mask map,,
|
||||
inspector.featureLevel.keyword._MASKMAP3_ON.effect,启用第三张遮罩贴图,Uses the third mask map,,
|
||||
inspector.featureLevel.keyword._MATCAP.effect,增加 MatCap 贴图光照,Adds matcap lighting texture,,
|
||||
inspector.featureLevel.keyword._NOISEMAP.effect,使用噪声贴图产生扭曲,Uses a noise map for distortion,,
|
||||
inspector.featureLevel.keyword._NOISE_MASKMAP.effect,使用遮罩限制扭曲范围,Masks the distortion effect,,
|
||||
inspector.featureLevel.keyword._NORMALMAP.effect,使用法线贴图增加光照细节,Uses normal map lighting detail,,
|
||||
inspector.featureLevel.keyword._PARALLAX_MAPPING.effect,偏移 UV 模拟视差深度,Offsets UVs for parallax depth,,
|
||||
inspector.featureLevel.keyword._PARCUSTOMDATA_ON.effect,读取粒子 Custom Data 通道,Reads particle custom data channels,,
|
||||
inspector.featureLevel.keyword._PROGRAM_NOISE.effect,在 Shader 中生成程序噪声,Generates procedural noise in shader,,
|
||||
inspector.featureLevel.keyword._PROGRAM_NOISE_SIMPLE.effect,启用简单程序噪声,Enables simple procedural noise,,
|
||||
inspector.featureLevel.keyword._PROGRAM_NOISE_VORONOI.effect,启用 Voronoi 程序噪声,Enables Voronoi procedural noise,,
|
||||
inspector.featureLevel.keyword._SCREEN_DISTORT_MODE.effect,采样屏幕纹理实现扭曲,Samples screen texture for distortion,,
|
||||
inspector.featureLevel.keyword._SCRIPTABLETIME.effect,使用脚本传入的时间值,Uses script provided time value,,
|
||||
inspector.featureLevel.keyword._SHARED_UV.effect,共享一组通用 UV 变换,Shares a common UV transform,,
|
||||
inspector.featureLevel.keyword._SOFTPARTICLES_ON.effect,使用场景深度柔化粒子边缘,Softens particles using scene depth,,
|
||||
inspector.featureLevel.keyword._SPECULAR_COLOR.effect,增加高光颜色控制,Adds specular color highlight,,
|
||||
inspector.featureLevel.keyword._STENCIL_WITHOUT_PLAYER.effect,使用不含 Player 的模板遮罩,Uses stencil without player mask,,
|
||||
inspector.featureLevel.keyword._TYFLOW_VAT_ABSOLUTE.effect,使用 TyFlow 绝对 VAT 模式,Uses TyFlow absolute VAT mode,,
|
||||
inspector.featureLevel.keyword._TYFLOW_VAT_RELATIVE.effect,使用 TyFlow 相对 VAT 模式,Uses TyFlow relative VAT mode,,
|
||||
inspector.featureLevel.keyword._TYFLOW_VAT_SKIN_PR.effect,使用 TyFlow Skin PR VAT 模式,Uses TyFlow skin PR VAT mode,,
|
||||
inspector.featureLevel.keyword._TYFLOW_VAT_SKIN_PRSAVE.effect,使用 TyFlow Skin PR Save VAT 模式,Uses TyFlow skin PR save VAT mode,,
|
||||
inspector.featureLevel.keyword._TYFLOW_VAT_SKIN_PRSXYZ.effect,使用 TyFlow Skin PRSXYZ VAT 模式,Uses TyFlow skin PRSXYZ VAT mode,,
|
||||
inspector.featureLevel.keyword._TYFLOW_VAT_SKIN_R.effect,使用 TyFlow Skin R VAT 模式,Uses TyFlow skin R VAT mode,,
|
||||
inspector.featureLevel.keyword._UNSCALETIME.effect,使用不受 TimeScale 影响的时间,Uses unscaled time,,
|
||||
inspector.featureLevel.keyword._VAT.effect,启用顶点动画贴图播放,Enables vertex animation texture playback,,
|
||||
inspector.featureLevel.keyword._VAT_HOUDINI.effect,使用 Houdini VAT 数据布局,Uses Houdini VAT data layout,,
|
||||
inspector.featureLevel.keyword._VAT_TYFLOW.effect,使用 TyFlow VAT 数据布局,Uses TyFlow VAT data layout,,
|
||||
inspector.featureLevel.keyword._VERTEX_OFFSET.effect,在 Shader 中偏移顶点,Offsets vertices in the shader,,
|
||||
inspector.featureLevel.keyword._VERTEX_OFFSET_MASKMAP.effect,用贴图遮罩顶点偏移范围,Masks vertex offset by texture,,
|
||||
inspector.featureLevel.saveCurrentAsDefault.button,保存当前配置为默认,Save Current Config as Default,将当前四个等级的 Keyword 配置写入 Package 默认 LevelAsset,Write the current tier keyword matrix into the package default LevelAsset
|
||||
inspector.featureLevel.saveCurrentAsDefault.failedTitle,保存默认配置失败,Save Default Config Failed,,
|
||||
inspector.featureLevel.saveCurrentAsDefault.failedMessage,无法写入 Package 默认 LevelAsset。请确认 Package 路径可写。,Could not write the package default LevelAsset. Please make sure the package path is writable.,,
|
||||
inspector.featureLevel.dialog.ok,确定,OK,,
|
||||
inspector.debugSymbols.providerLabel,NBShader2 调试符号,NBShader2 Debug Symbols,,
|
||||
inspector.debugSymbols.help.message,通过改写 NBShader.shader 引用的包内 include 控制 NBShader2 编译调试符号。仅在外部工具调试 Shader 时开启。,Controls NBShader2 shader compiler debug symbols by rewriting the package include used by NBShader.shader. Enable only while debugging external shader tools.,,
|
||||
inspector.debugSymbols.includeMismatch.message,ProjectSettings 值和包内 include 内容不一致。应用当前设置以重新同步 Shader 编译输入。,The ProjectSettings value and package include content do not match. Apply the current setting to resync the shader compiler input.,,
|
||||
inspector.debugSymbols.enable.label,启用 NBShader2 调试符号,Enable NBShader2 Debug Symbols,将 #pragma enable_d3d11_debug_symbols 写入 NBShader2 调试 pragma include,Writes #pragma enable_d3d11_debug_symbols into the NBShader2 debug pragma include
|
||||
inspector.debugSymbols.dirtyWarning.message,启用此选项会修改 NB_FX 包内的 NBShaderDebugPragmas.hlsl。除非明确需要 Shader 调试符号,否则不要提交开启状态的 include。,Enabling this option modifies NBShaderDebugPragmas.hlsl in the NB_FX package. Do not commit the enabled include unless shader debug symbols are deliberately required.,,
|
||||
inspector.debugSymbols.cachingPreprocessor.label,Caching Shader Preprocessor,Caching Shader Preprocessor,#include_with_pragmas 需要启用 Caching Shader Preprocessor,#include_with_pragmas requires the Caching Shader Preprocessor
|
||||
inspector.debugSymbols.cachingPreprocessorWarning.message,#include_with_pragmas 需要启用 Caching Shader Preprocessor。使用 NBShader2 调试符号前请先启用。,#include_with_pragmas requires the Caching Shader Preprocessor. Enable it before using NBShader2 debug symbols.,,
|
||||
inspector.debugSymbols.settingsState.label,ProjectSettings 状态,ProjectSettings State,保存于 ProjectSettings/NBShaderFeatureLevels.asset 的状态,Saved state in ProjectSettings/NBShaderFeatureLevels.asset
|
||||
inspector.debugSymbols.includeState.label,Include 文件状态,Include File State,NBShader.shader 实际使用的包内 include 内容,Actual package include content used by NBShader.shader
|
||||
inspector.debugSymbols.status.enabled,已启用,Enabled,,
|
||||
inspector.debugSymbols.status.disabled,已关闭,Disabled,,
|
||||
inspector.debugSymbols.status.missing,缺失,Missing,,
|
||||
inspector.debugSymbols.undo.changeSetting,修改 NBShader2 调试符号,Change NBShader2 Debug Symbols,,
|
||||
inspector.debugSymbols.applyFailed.title,应用 NBShader2 调试符号失败,Apply NBShader2 Debug Symbols Failed,,
|
||||
inspector.debugSymbols.applyFailed.message,无法写入 NBShaderDebugPragmas.hlsl:,Could not write NBShaderDebugPragmas.hlsl: ,,
|
||||
inspector.debugSymbols.confirmEnable.title,启用 NBShader2 调试符号,Enable NBShader2 Debug Symbols,,
|
||||
inspector.debugSymbols.confirmEnable.message,此操作会把 #pragma enable_d3d11_debug_symbols 写入 NB_FX 包内 include。开启期间 Shader 体积可能增加并关闭优化。,This writes #pragma enable_d3d11_debug_symbols into the NB_FX package include. Shader size can increase and optimizations are disabled while it is enabled.,,
|
||||
inspector.debugSymbols.dialog.enable,启用,Enable,,
|
||||
inspector.debugSymbols.dialog.cancel,取消,Cancel,,
|
||||
inspector.debugSymbols.dialog.ok,确定,OK,,
|
||||
inspector.debugSymbols.applySetting.button,应用当前设置到 Include,Apply Current Setting To Include,,
|
||||
inspector.debugSymbols.pingInclude.button,选中调试开关include,Select Debug Toggle Include,,
|
||||
inspector.debugSymbols.enableCachingPreprocessor.button,启用 Caching Shader Preprocessor,Enable Caching Shader Preprocessor,,
|
||||
inspector.particleBaseMigration.providerLabel,ParticleBase 迁移,ParticleBase Migration,,
|
||||
inspector.particleBaseMigration.settingsHelp,扫描引用旧版 ParticleBase Shader GUID 的材质资源,确认后迁移到 NBShader2。转换前请先做好资产备份或版本管理。,Scan material assets that reference the legacy ParticleBase shader GUID and migrate them to NBShader2 after confirmation. Back up assets or commit to version control before converting.,,
|
||||
inspector.particleBaseMigration.openWindow.button,打开 ParticleBase 迁移窗口,Open ParticleBase Migration Window,,
|
||||
inspector.particleBaseMigration.windowTitle,ParticleBase 迁移,ParticleBase Migration,,
|
||||
inspector.particleBaseMigration.title,ParticleBase -> NBShader2,ParticleBase -> NBShader2,,
|
||||
inspector.particleBaseMigration.windowWarning,此操作会修改材质 Shader 绑定并同步材质状态。转换前请先做好资产备份或版本管理。工具不会提供自动回退。,This operation changes material shader assignments and synchronized material state. Back up assets or commit to version control before converting. The tool does not provide an automatic rollback.,,
|
||||
inspector.particleBaseMigration.scan.button,扫描 ParticleBase 材质,Scan ParticleBase Materials,,
|
||||
inspector.particleBaseMigration.selectAll.button,全选,Select All,,
|
||||
inspector.particleBaseMigration.selectNone.button,全不选,Select None,,
|
||||
inspector.particleBaseMigration.scanPrompt,点击扫描查找引用 ParticleBase Shader 资源 GUID 的 .mat 资源。Shader 名称只作为回退匹配。,Click Scan to find .mat assets that reference the ParticleBase shader asset GUID. Shader name is only used as a fallback.,,
|
||||
inspector.particleBaseMigration.noMaterials,没有找到 ParticleBase 材质资源。,No ParticleBase material assets were found.,,
|
||||
inspector.particleBaseMigration.listSummary,找到 {0} 个材质资源。已选择:{1},Found {0} material asset(s). Selected: {1},,
|
||||
inspector.particleBaseMigration.column.material,材质,Material,,
|
||||
inspector.particleBaseMigration.column.match,匹配方式,Match,,
|
||||
inspector.particleBaseMigration.column.path,路径,Path,,
|
||||
inspector.particleBaseMigration.ping.button,定位,Ping,,
|
||||
inspector.particleBaseMigration.convertSelected.button,转换选中的材质到 NBShader2,Convert Selected Materials To NBShader2,,
|
||||
inspector.particleBaseMigration.progress.scanning,正在扫描材质 {0}/{1},Scanning materials {0}/{1},,
|
||||
inspector.particleBaseMigration.scanCanceled,扫描已取消。取消前找到 {0} 个材质资源。,Scan canceled. Found {0} material asset(s) before canceling.,,
|
||||
inspector.particleBaseMigration.scanComplete,扫描完成。找到 {0} 个 ParticleBase 材质资源。,Scan complete. Found {0} ParticleBase material asset(s).,,
|
||||
inspector.particleBaseMigration.nbShader2Missing.title,缺少 NBShader2,NBShader2 Missing,,
|
||||
inspector.particleBaseMigration.nbShader2Missing.message,无法加载 NBShader2 Shader 资源,迁移无法继续。,Could not load NBShader2 shader asset. Migration cannot continue.,,
|
||||
inspector.particleBaseMigration.dialog.ok,确定,OK,,
|
||||
inspector.particleBaseMigration.progress.converting,正在转换 {0}/{1}:{2},Converting {0}/{1}: {2},,
|
||||
inspector.particleBaseMigration.failuresLogged,ParticleBase 迁移完成但存在失败项:,ParticleBase migration completed with failures:,,
|
||||
inspector.particleBaseMigration.failure.loadMaterial,{0}:无法加载材质。,{0}: material could not be loaded.,,
|
||||
inspector.particleBaseMigration.failure.noLongerParticleBase,{0}:已跳过,因为它不再引用 ParticleBase。,{0}: skipped because it no longer references ParticleBase.,,
|
||||
inspector.particleBaseMigration.failure.exception,{0}:{1},{0}: {1},,
|
||||
inspector.particleBaseMigration.match.converted,已转换,Converted,,
|
||||
inspector.particleBaseMigration.confirm.title,确认 ParticleBase 迁移,Confirm ParticleBase Migration,,
|
||||
inspector.particleBaseMigration.confirm.message,即将把 {0} 个材质资源从 ParticleBase 转换到 NBShader2。继续前请先做好资产备份或版本管理。工具不会提供自动回退。,You are about to convert {0} material asset(s) from ParticleBase to NBShader2. Back up assets or commit to version control before continuing. This tool does not provide an automatic rollback.,,
|
||||
inspector.particleBaseMigration.dialog.convert,转换,Convert,,
|
||||
inspector.particleBaseMigration.dialog.cancel,取消,Cancel,,
|
||||
inspector.particleBaseMigration.conversionCanceled,转换已取消。已转换 {0}/{1} 个选中材质资源。,Conversion canceled. Converted {0}/{1} selected material asset(s).,,
|
||||
inspector.particleBaseMigration.conversionComplete,转换完成。已转换 {0}/{1} 个选中材质资源。,Conversion complete. Converted {0}/{1} selected material asset(s).,,
|
||||
inspector.particleBaseMigration.conversionFailures,{0} 个失败项已记录到 Console。,{0} failure(s) were logged to the Console.,,
|
||||
inspector.particleBaseMigration.match.shaderGuid,Shader GUID,Shader GUID,,
|
||||
inspector.particleBaseMigration.match.shaderObject,Shader 对象,Shader object,,
|
||||
inspector.particleBaseMigration.match.shaderNameFallback,Shader 名称回退,Shader name fallback,,
|
||||
inspector.particleBaseMigration.undo,迁移 ParticleBase 材质到 NBShader2,Migrate ParticleBase Materials To NBShader2,,
|
||||
|
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4463b85718cfaeb4cb989148b4548e0a
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,339 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using NBShaderEditor;
|
||||
using NBShaders2.Editor.FeatureLevel;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBShaders2.Editor
|
||||
{
|
||||
[InitializeOnLoad]
|
||||
internal static class NBShaderDebugSymbolsSettingsProvider
|
||||
{
|
||||
internal const string DebugIncludeAssetPath = "Packages/com.xuanxuan.nb.fx/NBShaders2/Shader/HLSL/NBShaderDebugPragmas.hlsl";
|
||||
private const string ShaderAssetPath = "Packages/com.xuanxuan.nb.fx/NBShaders2/Shader/NBShader.shader";
|
||||
private const string PackageJsonAssetPath = "Packages/com.xuanxuan.nb.fx/package.json";
|
||||
private const string PackageRootAssetPath = "Packages/com.xuanxuan.nb.fx/";
|
||||
|
||||
private static readonly Encoding Utf8NoBom = new UTF8Encoding(false);
|
||||
|
||||
private const string DisabledIncludeContent =
|
||||
"// NBShader2 shader compiler debug switches.\n" +
|
||||
"// This file is intentionally included with #include_with_pragmas.\n" +
|
||||
"\n" +
|
||||
"#undef NB_SHADER_DEBUG_SYMBOLS\n" +
|
||||
"#define NB_SHADER_DEBUG_SYMBOLS 0\n";
|
||||
|
||||
private const string EnabledIncludeContent =
|
||||
"// NBShader2 shader compiler debug switches.\n" +
|
||||
"// This file is intentionally included with #include_with_pragmas.\n" +
|
||||
"\n" +
|
||||
"#undef NB_SHADER_DEBUG_SYMBOLS\n" +
|
||||
"#define NB_SHADER_DEBUG_SYMBOLS 1\n" +
|
||||
"#pragma enable_d3d11_debug_symbols\n";
|
||||
|
||||
private enum IncludeDebugState
|
||||
{
|
||||
Missing,
|
||||
Disabled,
|
||||
Enabled
|
||||
}
|
||||
|
||||
static NBShaderDebugSymbolsSettingsProvider()
|
||||
{
|
||||
NBFXProjectSettings.RegisterSettingsSection(
|
||||
"NBShaderDebugSymbols",
|
||||
() => new GUIContent(Text("debugSymbols.providerLabel", "NBShader2 Debug Symbols")),
|
||||
OnGUI,
|
||||
new[]
|
||||
{
|
||||
"NBShader",
|
||||
"Debug",
|
||||
"Symbols",
|
||||
"D3D11",
|
||||
"enable_d3d11_debug_symbols"
|
||||
},
|
||||
90);
|
||||
}
|
||||
|
||||
private static void OnGUI(string searchContext)
|
||||
{
|
||||
var settings = NBShaderFeatureLevelProjectSettings.instance;
|
||||
settings.EnsureInitialized();
|
||||
var includeState = GetIncludeDebugState();
|
||||
var includeEnabled = includeState == IncludeDebugState.Enabled;
|
||||
|
||||
EditorGUILayout.HelpBox(
|
||||
Text(
|
||||
"debugSymbols.help.message",
|
||||
"Controls NBShader2 shader compiler debug symbols by rewriting the package include used by NBShader.shader. Enable only while debugging external shader tools."),
|
||||
MessageType.Info);
|
||||
|
||||
DrawCachingShaderPreprocessorState();
|
||||
DrawStateRows(settings.enableDebugSymbols, includeState);
|
||||
|
||||
if (settings.enableDebugSymbols != includeEnabled || includeState == IncludeDebugState.Missing)
|
||||
{
|
||||
EditorGUILayout.HelpBox(
|
||||
Text(
|
||||
"debugSymbols.includeMismatch.message",
|
||||
"The ProjectSettings value and package include content do not match. Apply the current setting to resync the shader compiler input."),
|
||||
MessageType.Warning);
|
||||
|
||||
if (GUILayout.Button(ButtonContent("debugSymbols.applySetting", "Apply Current Setting To Include")))
|
||||
ApplySettingToInclude(settings, settings.enableDebugSymbols);
|
||||
}
|
||||
|
||||
var enabled = settings.enableDebugSymbols;
|
||||
var toggleChanged = false;
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
enabled = EditorGUILayout.Toggle(
|
||||
Content(
|
||||
"debugSymbols.enable",
|
||||
"Enable NBShader2 Debug Symbols",
|
||||
"Writes #pragma enable_d3d11_debug_symbols into the NBShader2 debug pragma include."),
|
||||
settings.enableDebugSymbols);
|
||||
toggleChanged = EditorGUI.EndChangeCheck();
|
||||
|
||||
if (GUILayout.Button(ButtonContent("debugSymbols.pingInclude", "Select Debug Toggle Include"), GUILayout.Width(180f)))
|
||||
PingIncludeAsset();
|
||||
}
|
||||
|
||||
if (toggleChanged)
|
||||
{
|
||||
if (!enabled || ConfirmEnableDebugSymbols())
|
||||
ApplySettingToInclude(settings, enabled);
|
||||
}
|
||||
|
||||
EditorGUILayout.HelpBox(
|
||||
Text(
|
||||
"debugSymbols.dirtyWarning.message",
|
||||
"Enabling this option modifies NBShaderDebugPragmas.hlsl in the NB_FX package. Do not commit the enabled include unless shader debug symbols are deliberately required."),
|
||||
MessageType.Warning);
|
||||
|
||||
}
|
||||
|
||||
internal static bool WriteDebugInclude(bool enabled, out string error)
|
||||
{
|
||||
error = null;
|
||||
string physicalPath;
|
||||
if (!TryGetPhysicalPath(DebugIncludeAssetPath, out physicalPath, out error))
|
||||
return false;
|
||||
|
||||
var desiredContent = enabled ? EnabledIncludeContent : DisabledIncludeContent;
|
||||
try
|
||||
{
|
||||
var folder = Path.GetDirectoryName(physicalPath);
|
||||
if (!string.IsNullOrEmpty(folder) && !Directory.Exists(folder))
|
||||
Directory.CreateDirectory(folder);
|
||||
|
||||
if (File.Exists(physicalPath) && string.Equals(File.ReadAllText(physicalPath), desiredContent, StringComparison.Ordinal))
|
||||
{
|
||||
ReimportShaderAssets();
|
||||
return true;
|
||||
}
|
||||
|
||||
File.WriteAllText(physicalPath, desiredContent, Utf8NoBom);
|
||||
ReimportShaderAssets();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex.Message;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void DrawCachingShaderPreprocessorState()
|
||||
{
|
||||
var enabled = EditorSettings.cachingShaderPreprocessor;
|
||||
EditorGUILayout.LabelField(
|
||||
Content(
|
||||
"debugSymbols.cachingPreprocessor",
|
||||
"Caching Shader Preprocessor",
|
||||
"#include_with_pragmas requires the Caching Shader Preprocessor."),
|
||||
new GUIContent(enabled
|
||||
? Text("debugSymbols.status.enabled", "Enabled")
|
||||
: Text("debugSymbols.status.disabled", "Disabled")));
|
||||
|
||||
if (enabled)
|
||||
return;
|
||||
|
||||
EditorGUILayout.HelpBox(
|
||||
Text(
|
||||
"debugSymbols.cachingPreprocessorWarning.message",
|
||||
"#include_with_pragmas requires the Caching Shader Preprocessor. Enable it before using NBShader2 debug symbols."),
|
||||
MessageType.Warning);
|
||||
|
||||
if (GUILayout.Button(ButtonContent("debugSymbols.enableCachingPreprocessor", "Enable Caching Shader Preprocessor")))
|
||||
{
|
||||
EditorSettings.cachingShaderPreprocessor = true;
|
||||
ReimportShaderAssets();
|
||||
}
|
||||
}
|
||||
|
||||
private static void DrawStateRows(bool settingsEnabled, IncludeDebugState includeState)
|
||||
{
|
||||
EditorGUILayout.LabelField(
|
||||
Content("debugSymbols.settingsState", "ProjectSettings State", "Saved state in ProjectSettings/NBShaderFeatureLevels.asset."),
|
||||
new GUIContent(settingsEnabled
|
||||
? Text("debugSymbols.status.enabled", "Enabled")
|
||||
: Text("debugSymbols.status.disabled", "Disabled")));
|
||||
|
||||
EditorGUILayout.LabelField(
|
||||
Content("debugSymbols.includeState", "Include File State", "Actual package include content used by NBShader.shader."),
|
||||
new GUIContent(GetIncludeStateText(includeState)));
|
||||
}
|
||||
|
||||
private static void ApplySettingToInclude(NBShaderFeatureLevelProjectSettings settings, bool enabled)
|
||||
{
|
||||
Undo.RecordObject(settings, Text("debugSymbols.undo.changeSetting", "Change NBShader2 Debug Symbols"));
|
||||
|
||||
string error;
|
||||
if (WriteDebugInclude(enabled, out error))
|
||||
{
|
||||
if (settings.enableDebugSymbols != enabled)
|
||||
{
|
||||
settings.SetDebugSymbolsEnabled(enabled);
|
||||
settings.SaveDebugSymbolsProjectSettings();
|
||||
}
|
||||
|
||||
UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
|
||||
return;
|
||||
}
|
||||
|
||||
EditorUtility.DisplayDialog(
|
||||
Text("debugSymbols.applyFailed.title", "Apply NBShader2 Debug Symbols Failed"),
|
||||
Text("debugSymbols.applyFailed.message", "Could not write NBShaderDebugPragmas.hlsl: ") + error,
|
||||
Text("debugSymbols.dialog.ok", "OK"));
|
||||
}
|
||||
|
||||
private static bool ConfirmEnableDebugSymbols()
|
||||
{
|
||||
return EditorUtility.DisplayDialog(
|
||||
Text("debugSymbols.confirmEnable.title", "Enable NBShader2 Debug Symbols"),
|
||||
Text(
|
||||
"debugSymbols.confirmEnable.message",
|
||||
"This writes #pragma enable_d3d11_debug_symbols into the NB_FX package include. Shader size can increase and optimizations are disabled while it is enabled."),
|
||||
Text("debugSymbols.dialog.enable", "Enable"),
|
||||
Text("debugSymbols.dialog.cancel", "Cancel"));
|
||||
}
|
||||
|
||||
private static IncludeDebugState GetIncludeDebugState()
|
||||
{
|
||||
string physicalPath;
|
||||
string error;
|
||||
if (!TryGetPhysicalPath(DebugIncludeAssetPath, out physicalPath, out error) || !File.Exists(physicalPath))
|
||||
return IncludeDebugState.Missing;
|
||||
|
||||
try
|
||||
{
|
||||
return ContainsActiveDebugPragma(File.ReadAllText(physicalPath))
|
||||
? IncludeDebugState.Enabled
|
||||
: IncludeDebugState.Disabled;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return IncludeDebugState.Missing;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ContainsActiveDebugPragma(string content)
|
||||
{
|
||||
if (string.IsNullOrEmpty(content))
|
||||
return false;
|
||||
|
||||
using (var reader = new StringReader(content))
|
||||
{
|
||||
string line;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
var trimmed = line.TrimStart();
|
||||
if (trimmed.StartsWith("//", StringComparison.Ordinal))
|
||||
continue;
|
||||
|
||||
if (trimmed.StartsWith("#pragma", StringComparison.Ordinal) &&
|
||||
trimmed.IndexOf("enable_d3d11_debug_symbols", StringComparison.Ordinal) >= 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryGetPhysicalPath(string assetPath, out string physicalPath, out string error)
|
||||
{
|
||||
physicalPath = null;
|
||||
error = null;
|
||||
|
||||
var packageInfo = UnityEditor.PackageManager.PackageInfo.FindForAssetPath(assetPath);
|
||||
if (packageInfo == null)
|
||||
packageInfo = UnityEditor.PackageManager.PackageInfo.FindForAssetPath(PackageJsonAssetPath);
|
||||
|
||||
if (packageInfo == null || string.IsNullOrEmpty(packageInfo.resolvedPath))
|
||||
{
|
||||
error = "Could not resolve package path for " + assetPath;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!assetPath.StartsWith(PackageRootAssetPath, StringComparison.Ordinal))
|
||||
{
|
||||
error = "Asset path is outside NB_FX package: " + assetPath;
|
||||
return false;
|
||||
}
|
||||
|
||||
var relativePath = assetPath.Substring(PackageRootAssetPath.Length).Replace('/', Path.DirectorySeparatorChar);
|
||||
physicalPath = Path.Combine(packageInfo.resolvedPath, relativePath);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void ReimportShaderAssets()
|
||||
{
|
||||
AssetDatabase.ImportAsset(DebugIncludeAssetPath, ImportAssetOptions.ForceUpdate);
|
||||
AssetDatabase.ImportAsset(ShaderAssetPath, ImportAssetOptions.ForceUpdate);
|
||||
}
|
||||
|
||||
private static void PingIncludeAsset()
|
||||
{
|
||||
var asset = AssetDatabase.LoadMainAssetAtPath(DebugIncludeAssetPath);
|
||||
if (asset == null)
|
||||
return;
|
||||
|
||||
EditorGUIUtility.PingObject(asset);
|
||||
Selection.activeObject = asset;
|
||||
}
|
||||
|
||||
private static string GetIncludeStateText(IncludeDebugState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case IncludeDebugState.Enabled:
|
||||
return Text("debugSymbols.status.enabled", "Enabled");
|
||||
case IncludeDebugState.Disabled:
|
||||
return Text("debugSymbols.status.disabled", "Disabled");
|
||||
default:
|
||||
return Text("debugSymbols.status.missing", "Missing");
|
||||
}
|
||||
}
|
||||
|
||||
private static GUIContent Content(string key, string fallback, string tip)
|
||||
{
|
||||
return NBShaderInspectorLocalization.MakeInspectorContent(key, fallback, tip);
|
||||
}
|
||||
|
||||
private static GUIContent ButtonContent(string key, string fallback)
|
||||
{
|
||||
return NBShaderInspectorLocalization.MakeContent("inspector." + key + ".button", fallback);
|
||||
}
|
||||
|
||||
private static string Text(string key, string fallback)
|
||||
{
|
||||
return NBShaderInspectorLocalization.GetInspectorText(key, fallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e55769c18e7943dc94e2223f195e0311
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
138
Packages/NB_FX/NBShaders2/Editor/NBShaderGUI.cs
Normal file
138
Packages/NB_FX/NBShaders2/Editor/NBShaderGUI.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using NBShader;
|
||||
|
||||
namespace NBShaderEditor
|
||||
{
|
||||
public class NBShaderGUI : ShaderGUI
|
||||
{
|
||||
private NBShaderRootItem _rootItem;
|
||||
private string _currentLanguage;
|
||||
|
||||
public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
|
||||
{
|
||||
string currentLanguage = NBShaderInspectorLocalization.CurrentLanguage;
|
||||
if (_rootItem == null || !string.Equals(_currentLanguage, currentLanguage, System.StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_rootItem = new NBShaderRootItem();
|
||||
_currentLanguage = currentLanguage;
|
||||
}
|
||||
|
||||
_rootItem.OnGUI(materialEditor, properties);
|
||||
}
|
||||
}
|
||||
|
||||
public class NBShaderRootItem : ShaderGUIRootItem
|
||||
{
|
||||
public NBShaderGUIContext Context { get; private set; }
|
||||
public NBShaderSyncService SyncService { get; private set; }
|
||||
|
||||
private ModeBigBlockItem _modeBlock;
|
||||
private BaseOptionBigBlockItem _baseBlock;
|
||||
private MainTexBigBlockItem _mainTexBlock;
|
||||
private LightBigBlockItem _lightBlock;
|
||||
private FeatureBigBlockItem _featureBlock;
|
||||
private TABigBlockItem _taBlock;
|
||||
private ParticleVertexStreamsItem _particleVertexStreamsItem;
|
||||
private NBShaderGUIToolBar _toolBar;
|
||||
|
||||
public override void InitFlags(System.Collections.Generic.List<Material> mats)
|
||||
{
|
||||
ShaderFlags = new System.Collections.Generic.List<ShaderFlagsBase>();
|
||||
foreach (Material mat in mats)
|
||||
{
|
||||
ShaderFlags.Add(new NBShaderFlags(mat));
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnChildOnGUI()
|
||||
{
|
||||
if (Context == null)
|
||||
{
|
||||
Context = new NBShaderGUIContext(this);
|
||||
SyncService = new NBShaderSyncService(this);
|
||||
}
|
||||
|
||||
Context.Refresh();
|
||||
|
||||
if (IsInit)
|
||||
{
|
||||
_toolBar = new NBShaderGUIToolBar(this);
|
||||
_modeBlock = new ModeBigBlockItem(this, null);
|
||||
_baseBlock = new BaseOptionBigBlockItem(this, null);
|
||||
_mainTexBlock = new MainTexBigBlockItem(this, null);
|
||||
_lightBlock = new LightBigBlockItem(this, null);
|
||||
_featureBlock = new FeatureBigBlockItem(this, null);
|
||||
_taBlock = new TABigBlockItem(this, null);
|
||||
_particleVertexStreamsItem = new ParticleVertexStreamsItem(this, null);
|
||||
}
|
||||
|
||||
bool syncMaterialState = IsInit;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
_toolBar ??= new NBShaderGUIToolBar(this);
|
||||
_toolBar.DrawToolbar();
|
||||
|
||||
_modeBlock.OnGUI();
|
||||
_baseBlock.OnGUI();
|
||||
_mainTexBlock.OnGUI();
|
||||
if (Context.UIEffectEnabled == MixedBool.False)
|
||||
{
|
||||
_lightBlock.OnGUI();
|
||||
}
|
||||
|
||||
_featureBlock.OnGUI();
|
||||
_taBlock.OnGUI();
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
syncMaterialState = true;
|
||||
}
|
||||
|
||||
if (syncMaterialState)
|
||||
{
|
||||
SyncService.SyncMaterialState();
|
||||
}
|
||||
|
||||
_particleVertexStreamsItem.OnGUI();
|
||||
}
|
||||
|
||||
public void ExecuteResetAllItems()
|
||||
{
|
||||
_modeBlock?.ExecuteReset(true);
|
||||
_baseBlock?.ExecuteReset(true);
|
||||
_mainTexBlock?.ExecuteReset(true);
|
||||
_lightBlock?.ExecuteReset(true);
|
||||
_featureBlock?.ExecuteReset(true);
|
||||
_taBlock?.ExecuteReset(true);
|
||||
}
|
||||
|
||||
public System.Collections.Generic.IEnumerable<ShaderGUIItem> GetToolbarResetRootItems()
|
||||
{
|
||||
if (_baseBlock != null)
|
||||
{
|
||||
yield return _baseBlock;
|
||||
}
|
||||
|
||||
if (_mainTexBlock != null)
|
||||
{
|
||||
yield return _mainTexBlock;
|
||||
}
|
||||
|
||||
if (_lightBlock != null)
|
||||
{
|
||||
yield return _lightBlock;
|
||||
}
|
||||
|
||||
if (_featureBlock != null)
|
||||
{
|
||||
yield return _featureBlock;
|
||||
}
|
||||
|
||||
if (_taBlock != null)
|
||||
{
|
||||
yield return _taBlock;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Packages/NB_FX/NBShaders2/Editor/NBShaderGUI.cs.meta
Normal file
11
Packages/NB_FX/NBShaders2/Editor/NBShaderGUI.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1b18ce80080b45c7a5b2a0f8cc2492d7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
335
Packages/NB_FX/NBShaders2/Editor/NBShaderGUI_RULE.md
Normal file
335
Packages/NB_FX/NBShaders2/Editor/NBShaderGUI_RULE.md
Normal file
@@ -0,0 +1,335 @@
|
||||
# NBShaderGUI Rule
|
||||
|
||||
## Purpose
|
||||
|
||||
This rule defines the structure constraints for `NBShaders2/Editor/NBShaderGUI` and shared `ShaderGUIItems`.
|
||||
|
||||
## Core Principles
|
||||
|
||||
- Keep the GUI structure minimal and explicit.
|
||||
- Do not add wrapper abstractions without a clear responsibility.
|
||||
- `FoldOut` behavior and `parent-child composition` are two different concerns and must not be mixed.
|
||||
- Common drawing items live in `XuanXuanRenderUtility/Editor/ShaderGUIItems`.
|
||||
- Shared item names must not use the `NBShader` prefix.
|
||||
|
||||
## Shared Item Rules
|
||||
|
||||
All common material-property drawing items must live in `XuanXuanRenderUtility/Editor/ShaderGUIItems`, not in `NBShaders2/Editor/ShaderGUIItems`.
|
||||
|
||||
Use shared items for common material property drawing:
|
||||
|
||||
- `ToggleItem`
|
||||
- `ColorItem`
|
||||
- `TextureItem`
|
||||
- `Vector2LineItem`
|
||||
- `VectorComponentItem`
|
||||
- `HelpBoxItem`
|
||||
|
||||
Vector-related shared item classes should stay in one `VectorItem.cs` file.
|
||||
|
||||
Do not create NBShader-prefixed wrappers for common drawing behavior. These names are not allowed for shared/general controls:
|
||||
|
||||
- `NBShaderTogglePropertyItem`
|
||||
- `NBShaderColorPropertyItem`
|
||||
- `NBShaderTexturePropertyItem`
|
||||
- `NBShaderVector2LinePropertyItem`
|
||||
- `NBShaderVectorComponentPropertyItem`
|
||||
- `NBShaderHelpBoxItem`
|
||||
|
||||
Do not create wrappers that only set `PropertyName`, `GuiContent`, ranges, or visibility around an existing shared item. Prefer using the shared item directly, or create a concrete business item when there is real shader-specific behavior.
|
||||
|
||||
Pure configuration items should be instantiated directly inside the owning block constructor.
|
||||
|
||||
Use direct shared item construction for cases that only assign:
|
||||
|
||||
- `PropertyName`
|
||||
- `GuiContent` or `GUIContent` provider
|
||||
- `RangePropertyName`
|
||||
- `Min` / `Max`
|
||||
- simple visibility predicates
|
||||
|
||||
Example pattern:
|
||||
|
||||
```csharp
|
||||
_alphaAllItem = new ShaderGUISliderItem(rootItem, this)
|
||||
{
|
||||
PropertyName = "_AlphaAll",
|
||||
GuiContent = NBShaderInspectorLocalization.MakeInspectorContent("base.alphaAll", "Overall Alpha"),
|
||||
RangePropertyName = "AlphaAllRangeVec"
|
||||
};
|
||||
_alphaAllItem.InitTriggerByChild();
|
||||
```
|
||||
|
||||
Do not create a named class for this pattern, such as `AlphaAllItem`, unless it adds real behavior.
|
||||
|
||||
A concrete item class is justified only when it owns shader-specific behavior, such as:
|
||||
|
||||
- overriding `OnGUI`, `DrawBlock`, `OnEndChange`, `CheckIsPropertyModified`, or `ExecuteReset`
|
||||
- synchronizing keywords, render queue, blend state, pass state, runtime flags, or context
|
||||
- implementing a reusable layout/control behavior that belongs in shared `ShaderGUIItems`
|
||||
|
||||
Current examples:
|
||||
|
||||
- `ZTestItem` is allowed because it forces UIEffect depth behavior.
|
||||
- `AddToPreMultiplySlider` is allowed because its reset default depends on `BlendMode`.
|
||||
- Popup items are allowed when they update `Context`, keywords, render state, or child visibility.
|
||||
- `BaseColorIntensityItem`, `AlphaAllItem`, and `CutOffSlider` are not allowed as named wrappers because they only configure shared float/slider items.
|
||||
|
||||
Shared items must not reference:
|
||||
|
||||
- `NBShaderRootItem`
|
||||
- `NBShaderInspectorLocalization`
|
||||
- NBShader-specific keyword, pass, or flag logic
|
||||
|
||||
NBShader-specific labels should be passed into shared items through `GUIContent` providers from the call site.
|
||||
|
||||
## Localization Rules
|
||||
|
||||
NBShader Inspector user-facing text must go through `NBShaderInspectorLocalization`.
|
||||
|
||||
- Use CSV keys in `NBShaders2/Editor/Localization/NBShaderInspectorLocalization.csv`.
|
||||
- CSV format is `key,zh-CN,en-US,zh-CN-tip,en-US-tip`.
|
||||
- Tooltip text belongs to the same row as its label key. If a row has no tooltip, leave the tip columns empty.
|
||||
- Legacy `.tip` rows are only compatibility fallback for existing non-label message reads. New label tooltips must not create a separate `.tip` key.
|
||||
- Language selection is project-wide and stored by `NBFXProjectSettings` in `ProjectSettings/NB_FXSettings.asset`.
|
||||
- Language selection is edited through Project Settings > Project > NB_FX, not through Unity menu items.
|
||||
|
||||
Fallback order:
|
||||
|
||||
- current language
|
||||
- `zh-CN`
|
||||
- code fallback
|
||||
|
||||
Key conventions:
|
||||
|
||||
- normal labels use `inspector.<block>.<feature>.<name>.label`
|
||||
- label tooltips use the same key as the label and read the `<language>-tip` column
|
||||
- standalone tooltip/help text that is not attached to a `GUIContent` should use `.message` or another explicit text key
|
||||
- pure messages use `inspector.<block>.<feature>.<name>.message`
|
||||
- buttons use `inspector.<block>.<feature>.<name>.button`
|
||||
- popup options use `inspector.<block>.<feature>.<name>.option.<index>`
|
||||
|
||||
Code conventions:
|
||||
|
||||
- Normal controls should call `NBShaderInspectorLocalization.MakeInspectorContent(key, fallback, tip)` or pass a provider that calls it. The helper reads the label from `inspector.<key>.label` and the tooltip from the same CSV row first.
|
||||
- HelpBox/message strings should call `NBShaderInspectorLocalization.GetInspectorText(key, fallback)`.
|
||||
- Popup options should call `NBShaderInspectorLocalization.GetInspectorOptions(key, fallbackArray)` or update `PopUpNames` from an options provider.
|
||||
- Shared `XuanXuanRenderUtility/Editor/ShaderGUIItems` must not reference `NBShaderInspectorLocalization`.
|
||||
- Shared items that draw visible labels should expose `GUIContent` providers; NBShader call sites provide localized content.
|
||||
- Technical identifiers stay untranslated unless they are deliberately shown as UI copy: shader property names, keyword names, pass names, enum protocol values, render queue numbers, and stencil config keys remain stable.
|
||||
|
||||
## NBShaders2 Item Rules
|
||||
|
||||
`NBShaders2/Editor/ShaderGUIItems` should only contain NBShader business structure:
|
||||
|
||||
- top-level blocks
|
||||
- secondary business blocks
|
||||
- shader-specific popup items with side effects
|
||||
- composite layout items such as the main texture layout
|
||||
- feature-specific items that synchronize NBShader keywords, pass state, or runtime flags
|
||||
|
||||
NBShader-specific item names are acceptable only when the item has NBShader business meaning. They must not be used for generic drawing controls.
|
||||
|
||||
## Inheritance Rules
|
||||
|
||||
### 1. `ShaderGUIItem`
|
||||
|
||||
`ShaderGUIItem` is the base class for normal GUI items.
|
||||
|
||||
Use it for:
|
||||
|
||||
- leaf controls
|
||||
- composite layout items
|
||||
- parent-child grouping items without foldout
|
||||
- texture layout parent items
|
||||
|
||||
Important:
|
||||
|
||||
- parent-child composition already belongs to `ShaderGUIItem`
|
||||
- do not introduce extra “control base class” layers just to express parent-child grouping
|
||||
|
||||
### 2. `BlockItem`
|
||||
|
||||
`BlockItem` exists only to provide `FoldOut` behavior.
|
||||
|
||||
Responsibilities:
|
||||
|
||||
- owns `FoldOutPropertyName`
|
||||
- owns foldout open/close lifecycle
|
||||
- draws foldout title row
|
||||
- draws children inside foldout content area
|
||||
|
||||
It must not be used for:
|
||||
|
||||
- non-foldout layout items
|
||||
- generic feature/control wrappers
|
||||
|
||||
### 3. `BigBlockItem`
|
||||
|
||||
`BigBlockItem` inherits from `BlockItem`.
|
||||
|
||||
Responsibilities:
|
||||
|
||||
- defines the visual style of top-level blocks
|
||||
- bold title
|
||||
- spacing
|
||||
- separator line
|
||||
|
||||
`BigBlockItem` is a style specialization of `BlockItem`, not a different composition system.
|
||||
|
||||
## Top-Level Block Rules
|
||||
|
||||
These top-level sections should inherit `BigBlockItem`:
|
||||
|
||||
- `ModeBigBlockItem`
|
||||
- `BaseOptionBigBlockItem`
|
||||
- `MainTexBigBlockItem`
|
||||
- `LightBigBlockItem`
|
||||
- `FeatureBigBlockItem`
|
||||
- `TABigBlockItem`
|
||||
|
||||
These are first-level inspector sections and should use persisted foldout state.
|
||||
|
||||
## Secondary Block Rules
|
||||
|
||||
Only use `BlockItem` for a secondary section when:
|
||||
|
||||
- it has its own foldout state
|
||||
- it is semantically a collapsible section
|
||||
- it contains a group of child items
|
||||
|
||||
If a section is only doing layout and grouping, it must stay on `ShaderGUIItem`.
|
||||
|
||||
## Layout Rules
|
||||
|
||||
All normal inspector row layout must be owned by `ShaderGUIItem`.
|
||||
|
||||
Do not calculate label/control/reset positions independently in leaf items. Use the base rect pipeline:
|
||||
|
||||
- `GetRect()`
|
||||
- `ApplyGlobalRectCompensation()`
|
||||
- `SplitLineRect()`
|
||||
- `SplitControlAndResetRect()`
|
||||
|
||||
Standard row responsibilities:
|
||||
|
||||
- `BaseRect` is the compensated full row rect.
|
||||
- `LabelRect` uses `GetLabelWidth(BaseRect)`.
|
||||
- `GetLabelWidth()` has a fixed minimum (`MinLabelWidth`) and then grows by `LabelWidthRatio` once the inspector row is wide enough.
|
||||
- Do not read or hardcode a fixed label width in business items. If a custom layout needs the normal label/control split, call `SplitLineRect()`.
|
||||
- `ControlRect` starts after `LabelRect` and reserves reset space.
|
||||
- `ResetRect` is fixed to the right side and uses `ResetButtonSize`.
|
||||
- `ControlResetGap` is the only standard gap between control and reset.
|
||||
|
||||
Do not add local magic numbers for reset width, reset gap, or standard control expansion in business items. If a layout constant is needed by more than one item, add it to `ShaderGUIItem`.
|
||||
|
||||
Global row compensation:
|
||||
|
||||
- `GlobalRectXOffset` shifts the whole row left/right.
|
||||
- `GlobalRectWidthExpansion` expands the whole row width.
|
||||
- These constants compensate meaningless inspector-side margin and must be applied at the rect entry point, before splitting label/control/reset.
|
||||
|
||||
Any item that calls `EditorGUILayout.GetControlRect()` directly must immediately pass the result through `ApplyGlobalRectCompensation()`, unless there is a specific documented reason not to.
|
||||
|
||||
Editor indent rules:
|
||||
|
||||
- `EditorGUI.indentLevel` is only a hierarchy level counter.
|
||||
- Do not introduce a separate per-block indent-count constant.
|
||||
- Unity's internal per-level indent width is represented by `UnityEditorGUIIndentWidth`.
|
||||
- The desired NBShader GUI visual indent width is represented by `EditorGUIIndentWidth`.
|
||||
- `ApplyLabelIndentWidth()` converts Unity's built-in `EditorGUI.LabelField` indent width into the desired visual indent width for the NBShader GUI rect system.
|
||||
- Direct `GUI.Label`, `GUI.Toggle`, texture preview rects, and other non-`EditorGUI.LabelField` labels do not get Unity's built-in label indent. Use `ApplyDirectLabelIndentWidth()` for their label start.
|
||||
- Foldout arrows must be positioned from the final visual label text x, not from `BaseRect.x`. Use `ShaderGUIFoldOutHelper` / `GetEditorLabelTextX()` and do not call `EditorGUI.Foldout()` directly in business items.
|
||||
- Do not hardcode a local texture or foldout indent width. Three-line texture groups must use `ApplyDirectLabelIndentWidth()` for the texture preview left edge and `SplitLineRect()` for Tilling/Offset control/reset positions.
|
||||
|
||||
Control indent compensation:
|
||||
|
||||
- `ControlIndentCompensation` is not the same thing as row indent.
|
||||
- It only adjusts `ControlRect` by moving `x` left and increasing `width`.
|
||||
- Use it only to compensate controls whose Unity internal drawing is shifted right inside the passed rect.
|
||||
- Do not apply it to controls that draw exactly inside the passed rect, such as single-line `ColorField`; call `GetRect(false)` or `SplitLineRect(..., false)` for those rows.
|
||||
- `EditorGUI.ColorField` draws the visible swatch through `EditorStyles.colorField.padding.Remove(position)`. Labeled color rows may compensate this padding inside `ColorItem`; no-label color rows keep their own local inset. Do not add Color-only constants to `ShaderGUIItem`.
|
||||
- Keep `LabelRect` and `ResetRect` independent from this compensation.
|
||||
- Composite layouts may disable it with `applyControlIndentCompensation: false` and then apply a local compensation only to the actual control rect when needed.
|
||||
|
||||
Full-width or no-label rows:
|
||||
|
||||
- Rows such as full-width color bars should not automatically use label/control semantics.
|
||||
- If a row has no label, call `SplitControlAndResetRect()` directly and disable control compensation when the row must preserve its left edge.
|
||||
- Reset alignment should still come from `SplitControlAndResetRect()`.
|
||||
|
||||
Texture rows:
|
||||
|
||||
- All texture object inputs should use the shared three-row texture layout (`TexturePropertyGroupItem` through `TextureItem`) instead of `MaterialEditor.TexturePropertySingleLine()`.
|
||||
- Texture rows without editable Tilling/Offset should still reserve the three-row texture object area and leave the Tilling/Offset rows empty.
|
||||
- Texture color rows remain separate no-label color rows after the three-row texture object group.
|
||||
|
||||
## Animated Property Rules
|
||||
|
||||
Animated property highlight must follow Unity's native material inspector behavior.
|
||||
|
||||
Do not manually set animated controls to a fixed color such as `Color.red`.
|
||||
|
||||
Use the shared `ShaderGUIItem` animated scope helpers:
|
||||
|
||||
- `BeginAnimatedPropertyBackground(Rect totalPosition, MaterialProperty property)`
|
||||
- `EndAnimatedPropertyBackground(bool scopeActive)`
|
||||
|
||||
These helpers must use `MaterialEditor.BeginAnimatedCheck()` and `MaterialEditor.EndAnimatedCheck()`. They must not implement their own color decision with `AnimationMode.IsPropertyAnimated()`.
|
||||
|
||||
Reason:
|
||||
|
||||
- Unity's original `MaterialEditor.ShaderProperty()` wraps controls with `BeginAnimatedCheck()` / `EndAnimatedCheck()`.
|
||||
- Unity chooses between animated, recorded, and candidate colors internally.
|
||||
- Unity restores the previous `GUI.backgroundColor` through a stack, so nested drawing does not leak color state.
|
||||
|
||||
Standard item rule:
|
||||
|
||||
- Normal leaf items that use `ShaderGUIItem.OnGUI()` should rely on the base animated scope around `DrawController()`.
|
||||
|
||||
Custom `OnGUI()` rule:
|
||||
|
||||
- If an item overrides `OnGUI()` or draws multiple independent controls manually, it must explicitly wrap each actual editable control with the correct `MaterialProperty`.
|
||||
- Do not wrap a composite row with one property if the row contains controls backed by different properties.
|
||||
- Always call `EndAnimatedPropertyBackground()` in the same draw path after the control is drawn.
|
||||
|
||||
Multi-property control rule:
|
||||
|
||||
- `ShaderGUISliderItem` with `RangePropertyName` must wrap the min/max fields with the range vector property and the slider with the main float property.
|
||||
- Texture scale/offset controls must wrap their `Vector2Field` controls with the texture property when editing `textureScaleAndOffset`, or with the vector property when editing a vector ST property.
|
||||
|
||||
Animation refresh rule:
|
||||
|
||||
- `ShaderGUIRootItem` must update incoming `MaterialProperty[]` into `PropertyInfoDic` every `OnGUI`.
|
||||
- In animation mode, the material inspector should repaint so animated values and highlight state refresh while scrubbing or recording.
|
||||
- Do not cache animated-state booleans. Caching property path strings is allowed for diagnostics or non-background queries.
|
||||
|
||||
## Texture GUI Rules
|
||||
|
||||
For texture-related GUI:
|
||||
|
||||
- the texture layout parent item should inherit `ShaderGUIItem`
|
||||
- it is a layout/composition item, not a `BlockItem`
|
||||
- texture object field, texture color, and texture `Tilling/Offset` are separate GUI items
|
||||
- the parent texture layout item is responsible only for arranging these child items
|
||||
- texture layout must not move label text as a side effect of control compensation
|
||||
- for `Tilling/Offset`, calculate label rects from the uncompensated vector rect first, then apply control compensation only to the `Vector2Field` rect if visual control alignment requires it
|
||||
- texture, texture label, texture color, `Tilling/Offset`, and reset buttons must still use shared `ShaderGUIItem` layout constants where applicable
|
||||
- texture ST animated highlight must use Unity's texture `MaterialProperty` animated check, not manual `_ST.x/y/z/w` color logic
|
||||
|
||||
Required structure:
|
||||
|
||||
- texture parent layout item
|
||||
- texture object field item
|
||||
- texture color item
|
||||
- texture `Tilling/Offset` item
|
||||
|
||||
## Anti-Rules
|
||||
|
||||
Do not introduce these kinds of abstractions again unless there is a concrete need:
|
||||
|
||||
- generic `ControlItem` base classes with no real behavior
|
||||
- generic `FeatureItem` base classes with no real semantics
|
||||
- wrapper layers that only rename existing base classes
|
||||
|
||||
If an abstraction does not define a stable responsibility boundary, do not keep it.
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0faa7bb86fa23764794c77e95761379e
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
691
Packages/NB_FX/NBShaders2/Editor/ParticleBaseMigrationWindow.cs
Normal file
691
Packages/NB_FX/NBShaders2/Editor/ParticleBaseMigrationWindow.cs
Normal file
@@ -0,0 +1,691 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using NBShaderEditor;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBShaders2.Editor
|
||||
{
|
||||
[InitializeOnLoad]
|
||||
internal static class ParticleBaseMigrationSettingsProvider
|
||||
{
|
||||
static ParticleBaseMigrationSettingsProvider()
|
||||
{
|
||||
NBFXProjectSettings.RegisterSettingsSection(
|
||||
"ParticleBaseMigration",
|
||||
() => new GUIContent(Text("particleBaseMigration.providerLabel", "ParticleBase Migration")),
|
||||
OnGUI,
|
||||
new[]
|
||||
{
|
||||
"ParticleBase",
|
||||
"NBShader2",
|
||||
"Migration",
|
||||
"Material",
|
||||
"Shader GUID"
|
||||
},
|
||||
95);
|
||||
}
|
||||
|
||||
private static void OnGUI(string searchContext)
|
||||
{
|
||||
EditorGUILayout.HelpBox(
|
||||
Text(
|
||||
"particleBaseMigration.settingsHelp",
|
||||
"Scan material assets that reference the legacy ParticleBase shader GUID and migrate them to NBShader2 after confirmation. Back up assets or commit to version control before converting."),
|
||||
MessageType.Warning);
|
||||
|
||||
if (GUILayout.Button(ButtonContent("particleBaseMigration.openWindow", "Open ParticleBase Migration Window")))
|
||||
{
|
||||
ParticleBaseMigrationWindow.Open();
|
||||
}
|
||||
}
|
||||
|
||||
private static GUIContent ButtonContent(string key, string fallback)
|
||||
{
|
||||
return NBShaderInspectorLocalization.MakeContent("inspector." + key + ".button", fallback);
|
||||
}
|
||||
|
||||
private static string Text(string key, string fallback)
|
||||
{
|
||||
return NBShaderInspectorLocalization.GetInspectorText(key, fallback);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ParticleBaseMigrationWindow : EditorWindow
|
||||
{
|
||||
private const string LegacyShaderGuidFallback = "7184a95c20fc1a441a8815af4c795ccd";
|
||||
private const string NBShader2Guid = "7787bfdacec31472ca6644d6e3616bd4";
|
||||
private const string LegacyShaderAssetPath = "Packages/com.xuanxuan.nb.fx/NBShaders/Shader/ParticleBase.shader";
|
||||
private const string NBShader2AssetPath = "Packages/com.xuanxuan.nb.fx/NBShaders2/Shader/NBShader.shader";
|
||||
private const string LegacyShaderName = "Effects/NBShader(Legacy)";
|
||||
private const string UndoNameFallback = "Migrate ParticleBase Materials To NBShader2";
|
||||
|
||||
private static readonly string[] LegacyFoldoutProperties =
|
||||
{
|
||||
"_W9ParticleShaderGUIFoldToggle",
|
||||
"_W9ParticleShaderGUIFoldToggle1",
|
||||
"_W9ParticleShaderGUIFoldToggle2"
|
||||
};
|
||||
|
||||
private static readonly string[] NBShader2FoldoutProperties =
|
||||
{
|
||||
"_NBShaderGUIFoldToggle",
|
||||
"_NBShaderGUIFoldToggle1",
|
||||
"_NBShaderGUIFoldToggle2"
|
||||
};
|
||||
|
||||
private readonly List<MaterialMigrationEntry> _entries = new List<MaterialMigrationEntry>();
|
||||
private Vector2 _scrollPosition;
|
||||
private bool _scanned;
|
||||
private string _statusMessage;
|
||||
|
||||
public static void Open()
|
||||
{
|
||||
var window = GetWindow<ParticleBaseMigrationWindow>(Text("particleBaseMigration.windowTitle", "ParticleBase Migration"));
|
||||
window.minSize = new Vector2(760f, 420f);
|
||||
window.Show();
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
titleContent = new GUIContent(Text("particleBaseMigration.windowTitle", "ParticleBase Migration"));
|
||||
EditorGUILayout.LabelField(Text("particleBaseMigration.title", "ParticleBase -> NBShader2"), EditorStyles.boldLabel);
|
||||
EditorGUILayout.HelpBox(
|
||||
Text(
|
||||
"particleBaseMigration.windowWarning",
|
||||
"This operation changes material shader assignments and synchronized material state. Back up assets or commit to version control before converting. The tool does not provide an automatic rollback."),
|
||||
MessageType.Warning);
|
||||
|
||||
DrawToolbar();
|
||||
DrawStatus();
|
||||
DrawMaterialList();
|
||||
DrawConvertButton();
|
||||
}
|
||||
|
||||
private void DrawToolbar()
|
||||
{
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
if (GUILayout.Button(ButtonContent("particleBaseMigration.scan", "Scan ParticleBase Materials"), GUILayout.Height(24f)))
|
||||
{
|
||||
ScanMaterials();
|
||||
}
|
||||
|
||||
EditorGUI.BeginDisabledGroup(_entries.Count == 0);
|
||||
if (GUILayout.Button(ButtonContent("particleBaseMigration.selectAll", "Select All"), GUILayout.Width(96f), GUILayout.Height(24f)))
|
||||
{
|
||||
SetAllSelected(true);
|
||||
}
|
||||
|
||||
if (GUILayout.Button(ButtonContent("particleBaseMigration.selectNone", "Select None"), GUILayout.Width(96f), GUILayout.Height(24f)))
|
||||
{
|
||||
SetAllSelected(false);
|
||||
}
|
||||
|
||||
EditorGUI.EndDisabledGroup();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawStatus()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_statusMessage))
|
||||
{
|
||||
EditorGUILayout.HelpBox(_statusMessage, MessageType.Info);
|
||||
}
|
||||
else if (!_scanned)
|
||||
{
|
||||
EditorGUILayout.HelpBox(
|
||||
Text(
|
||||
"particleBaseMigration.scanPrompt",
|
||||
"Click Scan to find .mat assets that reference the ParticleBase shader asset GUID. Shader name is only used as a fallback."),
|
||||
MessageType.Info);
|
||||
}
|
||||
else if (_entries.Count == 0)
|
||||
{
|
||||
EditorGUILayout.HelpBox(
|
||||
Text("particleBaseMigration.noMaterials", "No ParticleBase material assets were found."),
|
||||
MessageType.Info);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawMaterialList()
|
||||
{
|
||||
if (_entries.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField(
|
||||
FormatText("particleBaseMigration.listSummary", "Found {0} material asset(s). Selected: {1}", _entries.Count, CountSelected()),
|
||||
EditorStyles.boldLabel);
|
||||
|
||||
using (new EditorGUILayout.HorizontalScope(EditorStyles.toolbar))
|
||||
{
|
||||
GUILayout.Label("", GUILayout.Width(24f));
|
||||
GUILayout.Label(Text("particleBaseMigration.column.material", "Material"), GUILayout.Width(240f));
|
||||
GUILayout.Label(Text("particleBaseMigration.column.match", "Match"), GUILayout.Width(160f));
|
||||
GUILayout.Label(Text("particleBaseMigration.column.path", "Path"), GUILayout.MinWidth(260f));
|
||||
}
|
||||
|
||||
_scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition);
|
||||
for (int i = 0; i < _entries.Count; i++)
|
||||
{
|
||||
DrawMaterialRow(_entries[i]);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
private static void DrawMaterialRow(MaterialMigrationEntry entry)
|
||||
{
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
entry.selected = EditorGUILayout.Toggle(entry.selected, GUILayout.Width(24f));
|
||||
EditorGUILayout.ObjectField(entry.material, typeof(Material), false, GUILayout.Width(240f));
|
||||
GUILayout.Label(entry.matchSource, EditorStyles.miniLabel, GUILayout.Width(160f));
|
||||
|
||||
if (GUILayout.Button(ButtonContent("particleBaseMigration.ping", "Ping"), GUILayout.Width(48f)))
|
||||
{
|
||||
PingAsset(entry.assetPath);
|
||||
}
|
||||
|
||||
EditorGUILayout.SelectableLabel(
|
||||
entry.assetPath,
|
||||
EditorStyles.miniLabel,
|
||||
GUILayout.Height(EditorGUIUtility.singleLineHeight),
|
||||
GUILayout.MinWidth(220f));
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawConvertButton()
|
||||
{
|
||||
EditorGUILayout.Space();
|
||||
EditorGUI.BeginDisabledGroup(CountSelected() == 0);
|
||||
if (GUILayout.Button(ButtonContent("particleBaseMigration.convertSelected", "Convert Selected Materials To NBShader2"), GUILayout.Height(30f)))
|
||||
{
|
||||
ConvertSelectedMaterials();
|
||||
}
|
||||
|
||||
EditorGUI.EndDisabledGroup();
|
||||
}
|
||||
|
||||
private void ScanMaterials()
|
||||
{
|
||||
_entries.Clear();
|
||||
_scanned = true;
|
||||
_statusMessage = null;
|
||||
|
||||
string legacyShaderGuid = ResolveShaderGuid(LegacyShaderAssetPath, LegacyShaderGuidFallback);
|
||||
Shader legacyShader = LoadShaderByGuidOrPath(legacyShaderGuid, LegacyShaderAssetPath);
|
||||
string[] materialGuids = AssetDatabase.FindAssets("t:Material");
|
||||
var seenPaths = new HashSet<string>(StringComparer.Ordinal);
|
||||
bool canceled = false;
|
||||
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < materialGuids.Length; i++)
|
||||
{
|
||||
if (EditorUtility.DisplayCancelableProgressBar(
|
||||
Text("particleBaseMigration.windowTitle", "ParticleBase Migration"),
|
||||
FormatText("particleBaseMigration.progress.scanning", "Scanning materials {0}/{1}", i + 1, materialGuids.Length),
|
||||
materialGuids.Length > 0 ? (float)i / materialGuids.Length : 1f))
|
||||
{
|
||||
canceled = true;
|
||||
break;
|
||||
}
|
||||
|
||||
string path = AssetDatabase.GUIDToAssetPath(materialGuids[i]);
|
||||
if (string.IsNullOrEmpty(path) ||
|
||||
!path.EndsWith(".mat", StringComparison.OrdinalIgnoreCase) ||
|
||||
!seenPaths.Add(path))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Material material = AssetDatabase.LoadAssetAtPath<Material>(path);
|
||||
string matchSource;
|
||||
if (!IsLegacyParticleBaseMaterial(path, material, legacyShaderGuid, legacyShader, out matchSource))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_entries.Add(new MaterialMigrationEntry
|
||||
{
|
||||
assetPath = path,
|
||||
material = material,
|
||||
selected = true,
|
||||
matchSource = matchSource
|
||||
});
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
|
||||
_entries.Sort((a, b) => string.CompareOrdinal(a.assetPath, b.assetPath));
|
||||
_statusMessage = canceled
|
||||
? FormatText("particleBaseMigration.scanCanceled", "Scan canceled. Found {0} material asset(s) before canceling.", _entries.Count)
|
||||
: FormatText("particleBaseMigration.scanComplete", "Scan complete. Found {0} ParticleBase material asset(s).", _entries.Count);
|
||||
}
|
||||
|
||||
private void ConvertSelectedMaterials()
|
||||
{
|
||||
List<MaterialMigrationEntry> selectedEntries = GetSelectedEntries();
|
||||
if (selectedEntries.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Shader targetShader = LoadShaderByGuidOrPath(NBShader2Guid, NBShader2AssetPath);
|
||||
if (targetShader == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog(
|
||||
Text("particleBaseMigration.nbShader2Missing.title", "NBShader2 Missing"),
|
||||
Text("particleBaseMigration.nbShader2Missing.message", "Could not load NBShader2 shader asset. Migration cannot continue."),
|
||||
Text("particleBaseMigration.dialog.ok", "OK"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ConfirmConversion(selectedEntries.Count))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string legacyShaderGuid = ResolveShaderGuid(LegacyShaderAssetPath, LegacyShaderGuidFallback);
|
||||
Shader legacyShader = LoadShaderByGuidOrPath(legacyShaderGuid, LegacyShaderAssetPath);
|
||||
int convertedCount = 0;
|
||||
bool canceled = false;
|
||||
var failures = new List<string>();
|
||||
int undoGroup = Undo.GetCurrentGroup();
|
||||
bool assetEditingStarted = false;
|
||||
string undoName = Text("particleBaseMigration.undo", UndoNameFallback);
|
||||
Undo.SetCurrentGroupName(undoName);
|
||||
|
||||
try
|
||||
{
|
||||
AssetDatabase.StartAssetEditing();
|
||||
assetEditingStarted = true;
|
||||
for (int i = 0; i < selectedEntries.Count; i++)
|
||||
{
|
||||
MaterialMigrationEntry entry = selectedEntries[i];
|
||||
if (EditorUtility.DisplayCancelableProgressBar(
|
||||
Text("particleBaseMigration.windowTitle", "ParticleBase Migration"),
|
||||
FormatText("particleBaseMigration.progress.converting", "Converting {0}/{1}: {2}", i + 1, selectedEntries.Count, entry.assetPath),
|
||||
selectedEntries.Count > 0 ? (float)i / selectedEntries.Count : 1f))
|
||||
{
|
||||
canceled = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ConvertMaterialEntry(entry, targetShader, legacyShaderGuid, legacyShader, undoName, failures))
|
||||
{
|
||||
convertedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (assetEditingStarted)
|
||||
{
|
||||
AssetDatabase.StopAssetEditing();
|
||||
}
|
||||
|
||||
EditorUtility.ClearProgressBar();
|
||||
Undo.CollapseUndoOperations(undoGroup);
|
||||
}
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
|
||||
if (failures.Count > 0)
|
||||
{
|
||||
Debug.LogWarning(
|
||||
Text("particleBaseMigration.failuresLogged", "ParticleBase migration completed with failures:") +
|
||||
"\n" +
|
||||
string.Join("\n", failures.ToArray()));
|
||||
}
|
||||
|
||||
_statusMessage = BuildConversionStatus(convertedCount, selectedEntries.Count, canceled, failures.Count);
|
||||
}
|
||||
|
||||
private static bool ConvertMaterialEntry(
|
||||
MaterialMigrationEntry entry,
|
||||
Shader targetShader,
|
||||
string legacyShaderGuid,
|
||||
Shader legacyShader,
|
||||
string undoName,
|
||||
List<string> failures)
|
||||
{
|
||||
try
|
||||
{
|
||||
Material material = AssetDatabase.LoadAssetAtPath<Material>(entry.assetPath);
|
||||
if (material == null)
|
||||
{
|
||||
failures.Add(FormatText("particleBaseMigration.failure.loadMaterial", "{0}: material could not be loaded.", entry.assetPath));
|
||||
return false;
|
||||
}
|
||||
|
||||
string matchSource;
|
||||
if (!IsLegacyParticleBaseMaterial(entry.assetPath, material, legacyShaderGuid, legacyShader, out matchSource))
|
||||
{
|
||||
failures.Add(FormatText("particleBaseMigration.failure.noLongerParticleBase", "{0}: skipped because it no longer references ParticleBase.", entry.assetPath));
|
||||
return false;
|
||||
}
|
||||
|
||||
int[] legacyFoldoutValues = CaptureLegacyFoldoutValues(material, entry.assetPath);
|
||||
Undo.RecordObject(material, undoName);
|
||||
material.shader = targetShader;
|
||||
ApplyNBShader2FoldoutValues(material, legacyFoldoutValues);
|
||||
NBShaderSyncService.SyncMaterialState(material);
|
||||
EditorUtility.SetDirty(material);
|
||||
AssetDatabase.SaveAssetIfDirty(material);
|
||||
|
||||
entry.material = material;
|
||||
entry.matchSource = Text("particleBaseMigration.match.converted", "Converted");
|
||||
entry.selected = false;
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
failures.Add(FormatText("particleBaseMigration.failure.exception", "{0}: {1}", entry.assetPath, ex.Message));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ConfirmConversion(int count)
|
||||
{
|
||||
return EditorUtility.DisplayDialog(
|
||||
Text("particleBaseMigration.confirm.title", "Confirm ParticleBase Migration"),
|
||||
FormatText(
|
||||
"particleBaseMigration.confirm.message",
|
||||
"You are about to convert {0} material asset(s) from ParticleBase to NBShader2.\n\nBack up assets or commit to version control before continuing. This tool does not provide an automatic rollback.",
|
||||
count),
|
||||
Text("particleBaseMigration.dialog.convert", "Convert"),
|
||||
Text("particleBaseMigration.dialog.cancel", "Cancel"));
|
||||
}
|
||||
|
||||
private static string BuildConversionStatus(int converted, int selected, bool canceled, int failureCount)
|
||||
{
|
||||
string status = canceled
|
||||
? FormatText("particleBaseMigration.conversionCanceled", "Conversion canceled. Converted {0}/{1} selected material asset(s).", converted, selected)
|
||||
: FormatText("particleBaseMigration.conversionComplete", "Conversion complete. Converted {0}/{1} selected material asset(s).", converted, selected);
|
||||
|
||||
if (failureCount > 0)
|
||||
{
|
||||
status += " " + FormatText("particleBaseMigration.conversionFailures", "{0} failure(s) were logged to the Console.", failureCount);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
private static int[] CaptureLegacyFoldoutValues(Material material, string assetPath)
|
||||
{
|
||||
var values = new int[LegacyFoldoutProperties.Length];
|
||||
for (int i = 0; i < LegacyFoldoutProperties.Length; i++)
|
||||
{
|
||||
values[i] = int.MinValue;
|
||||
if (material != null && material.HasProperty(LegacyFoldoutProperties[i]))
|
||||
{
|
||||
values[i] = material.GetInteger(LegacyFoldoutProperties[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
int serializedValue;
|
||||
if (TryReadSerializedIntegerProperty(assetPath, LegacyFoldoutProperties[i], out serializedValue))
|
||||
{
|
||||
values[i] = serializedValue;
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
private static void ApplyNBShader2FoldoutValues(Material material, int[] legacyValues)
|
||||
{
|
||||
if (material == null || legacyValues == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int count = Math.Min(legacyValues.Length, NBShader2FoldoutProperties.Length);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (legacyValues[i] == int.MinValue || !material.HasProperty(NBShader2FoldoutProperties[i]))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
material.SetInteger(NBShader2FoldoutProperties[i], legacyValues[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsLegacyParticleBaseMaterial(
|
||||
string assetPath,
|
||||
Material material,
|
||||
string legacyShaderGuid,
|
||||
Shader legacyShader,
|
||||
out string matchSource)
|
||||
{
|
||||
if (SerializedMaterialReferencesShaderGuid(assetPath, legacyShaderGuid))
|
||||
{
|
||||
matchSource = Text("particleBaseMigration.match.shaderGuid", "Shader GUID");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (material != null && material.shader != null)
|
||||
{
|
||||
if (legacyShader != null && material.shader == legacyShader)
|
||||
{
|
||||
matchSource = Text("particleBaseMigration.match.shaderObject", "Shader object");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (string.Equals(material.shader.name, LegacyShaderName, StringComparison.Ordinal))
|
||||
{
|
||||
matchSource = Text("particleBaseMigration.match.shaderNameFallback", "Shader name fallback");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
matchSource = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool SerializedMaterialReferencesShaderGuid(string assetPath, string shaderGuid)
|
||||
{
|
||||
if (string.IsNullOrEmpty(assetPath) || string.IsNullOrEmpty(shaderGuid))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string physicalPath;
|
||||
if (!TryGetPhysicalPath(assetPath, out physicalPath) || !File.Exists(physicalPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string content = File.ReadAllText(physicalPath);
|
||||
return content.IndexOf("m_Shader:", StringComparison.Ordinal) >= 0 &&
|
||||
content.IndexOf("guid: " + shaderGuid, StringComparison.OrdinalIgnoreCase) >= 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryReadSerializedIntegerProperty(string assetPath, string propertyName, out int value)
|
||||
{
|
||||
value = 0;
|
||||
if (string.IsNullOrEmpty(assetPath) || string.IsNullOrEmpty(propertyName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string physicalPath;
|
||||
if (!TryGetPhysicalPath(assetPath, out physicalPath) || !File.Exists(physicalPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string prefix = "- " + propertyName + ":";
|
||||
using (var reader = new StringReader(File.ReadAllText(physicalPath)))
|
||||
{
|
||||
string line;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
string trimmed = line.Trim();
|
||||
if (!trimmed.StartsWith(prefix, StringComparison.Ordinal))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string rawValue = trimmed.Substring(prefix.Length).Trim();
|
||||
return int.TryParse(rawValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out value);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryGetPhysicalPath(string assetPath, out string physicalPath)
|
||||
{
|
||||
physicalPath = null;
|
||||
if (string.IsNullOrEmpty(assetPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string normalizedPath = assetPath.Replace('\\', '/');
|
||||
if (normalizedPath.StartsWith("Assets/", StringComparison.Ordinal) ||
|
||||
string.Equals(normalizedPath, "Assets", StringComparison.Ordinal))
|
||||
{
|
||||
string projectRoot = Path.GetFullPath(Path.Combine(Application.dataPath, ".."));
|
||||
physicalPath = Path.Combine(projectRoot, normalizedPath.Replace('/', Path.DirectorySeparatorChar));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!normalizedPath.StartsWith("Packages/", StringComparison.Ordinal))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var packageInfo = UnityEditor.PackageManager.PackageInfo.FindForAssetPath(normalizedPath);
|
||||
if (packageInfo == null || string.IsNullOrEmpty(packageInfo.resolvedPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string packageRoot = "Packages/" + packageInfo.name + "/";
|
||||
if (!normalizedPath.StartsWith(packageRoot, StringComparison.Ordinal))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string relativePath = normalizedPath.Substring(packageRoot.Length).Replace('/', Path.DirectorySeparatorChar);
|
||||
physicalPath = Path.Combine(packageInfo.resolvedPath, relativePath);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string ResolveShaderGuid(string assetPath, string fallbackGuid)
|
||||
{
|
||||
string guid = AssetDatabase.AssetPathToGUID(assetPath);
|
||||
return string.IsNullOrEmpty(guid) ? fallbackGuid : guid;
|
||||
}
|
||||
|
||||
private static Shader LoadShaderByGuidOrPath(string guid, string fallbackAssetPath)
|
||||
{
|
||||
string assetPath = string.IsNullOrEmpty(guid) ? null : AssetDatabase.GUIDToAssetPath(guid);
|
||||
if (string.IsNullOrEmpty(assetPath))
|
||||
{
|
||||
assetPath = fallbackAssetPath;
|
||||
}
|
||||
|
||||
return AssetDatabase.LoadAssetAtPath<Shader>(assetPath);
|
||||
}
|
||||
|
||||
private void SetAllSelected(bool selected)
|
||||
{
|
||||
for (int i = 0; i < _entries.Count; i++)
|
||||
{
|
||||
_entries[i].selected = selected;
|
||||
}
|
||||
}
|
||||
|
||||
private int CountSelected()
|
||||
{
|
||||
int count = 0;
|
||||
for (int i = 0; i < _entries.Count; i++)
|
||||
{
|
||||
if (_entries[i].selected)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
private List<MaterialMigrationEntry> GetSelectedEntries()
|
||||
{
|
||||
var selectedEntries = new List<MaterialMigrationEntry>();
|
||||
for (int i = 0; i < _entries.Count; i++)
|
||||
{
|
||||
if (_entries[i].selected)
|
||||
{
|
||||
selectedEntries.Add(_entries[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return selectedEntries;
|
||||
}
|
||||
|
||||
private static void PingAsset(string assetPath)
|
||||
{
|
||||
UnityEngine.Object asset = AssetDatabase.LoadMainAssetAtPath(assetPath);
|
||||
if (asset == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Selection.activeObject = asset;
|
||||
EditorGUIUtility.PingObject(asset);
|
||||
}
|
||||
|
||||
private static GUIContent ButtonContent(string key, string fallback)
|
||||
{
|
||||
return NBShaderInspectorLocalization.MakeContent("inspector." + key + ".button", fallback);
|
||||
}
|
||||
|
||||
private static string Text(string key, string fallback)
|
||||
{
|
||||
return NBShaderInspectorLocalization.GetInspectorText(key, fallback);
|
||||
}
|
||||
|
||||
private static string FormatText(string key, string fallback, params object[] args)
|
||||
{
|
||||
return string.Format(Text(key, fallback), args);
|
||||
}
|
||||
|
||||
private sealed class MaterialMigrationEntry
|
||||
{
|
||||
public string assetPath;
|
||||
public Material material;
|
||||
public string matchSource;
|
||||
public bool selected;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 748a4df299d449b49d8ee8055fe56dea
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Packages/NB_FX/NBShaders2/Editor/ShaderGUIItems.meta
Normal file
8
Packages/NB_FX/NBShaders2/Editor/ShaderGUIItems.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 50e1a495cb24f82429902d4d23679f43
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,450 @@
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using NBShader;
|
||||
|
||||
namespace NBShaderEditor
|
||||
{
|
||||
public enum RenderFace
|
||||
{
|
||||
Front = 2,
|
||||
Back = 1,
|
||||
Both = 0
|
||||
}
|
||||
|
||||
public enum ForceZWriteMode
|
||||
{
|
||||
Default = 0,
|
||||
ForceOn = 1,
|
||||
ForceOff = 2
|
||||
}
|
||||
|
||||
public class BaseOptionBigBlockItem : BigBlockItem
|
||||
{
|
||||
private readonly NBShaderRootItem _nbRootItem;
|
||||
private readonly ShaderGUIFloatItem _baseColorIntensityItem;
|
||||
private readonly ShaderGUISliderItem _alphaAllItem;
|
||||
private readonly BlockItem _colorAdjustmentBlock;
|
||||
private readonly ToggleItem _colorAdjustmentOnlyMainTexItem;
|
||||
private readonly PropertyToggleBlockItem _hueShiftBlock;
|
||||
private readonly ShaderGUISliderItem _hueShiftSlider;
|
||||
private readonly CustomDataSelectItem _hueShiftCustomDataItem;
|
||||
private readonly PropertyToggleBlockItem _saturabilityBlock;
|
||||
private readonly ShaderGUISliderItem _saturabilitySlider;
|
||||
private readonly CustomDataSelectItem _saturabilityCustomDataItem;
|
||||
private readonly PropertyToggleBlockItem _contrastBlock;
|
||||
private readonly ColorItem _contrastMidColorItem;
|
||||
private readonly ShaderGUISliderItem _contrastSlider;
|
||||
private readonly CustomDataSelectItem _contrastCustomDataItem;
|
||||
private readonly PropertyToggleBlockItem _baseMapColorRefineBlock;
|
||||
private readonly VectorComponentItem _baseMapColorRefineA;
|
||||
private readonly VectorComponentItem _baseMapColorRefineBPower;
|
||||
private readonly VectorComponentItem _baseMapColorRefineBMultiply;
|
||||
private readonly VectorComponentItem _baseMapColorRefineLerp;
|
||||
private readonly ToggleItem _colorMultiAlphaItem;
|
||||
private readonly ZTestItem _zTestItem;
|
||||
private readonly CullModeItem _cullItem;
|
||||
private readonly ToggleItem _backFirstPassItem;
|
||||
private readonly ForceZWriteItem _forceZWriteItem;
|
||||
private readonly ToggleItem _affectsShadowsItem;
|
||||
private readonly ToggleItem _transparentShadowDitherItem;
|
||||
private readonly PropertyToggleBlockItem _baseBackColorBlock;
|
||||
private readonly ColorItem _baseBackColorItem;
|
||||
private readonly PropertyToggleBlockItem _distanceFadeBlock;
|
||||
private readonly Vector2LineItem _fadeRangeItem;
|
||||
private readonly PropertyToggleBlockItem _softParticlesBlock;
|
||||
private readonly Vector2LineItem _softParticleFadeItem;
|
||||
private readonly ToggleItem _stencilWithoutPlayerItem;
|
||||
private readonly ToggleItem _ignoreVertexColorItem;
|
||||
private readonly ShaderGUISliderItem _fogIntensityItem;
|
||||
|
||||
public BaseOptionBigBlockItem(NBShaderRootItem rootItem, ShaderGUIItem parentItem) :
|
||||
base(
|
||||
rootItem,
|
||||
parentItem,
|
||||
"_BaseOptionBigBlockItemFoldOut",
|
||||
() => Content("block.base", "Base Options", "Global controls"))
|
||||
{
|
||||
_nbRootItem = rootItem;
|
||||
|
||||
_baseColorIntensityItem = new ShaderGUIFloatItem(rootItem, this)
|
||||
{
|
||||
PropertyName = "_BaseColorIntensityForTimeline",
|
||||
GuiContent = Content("base.colorIntensity", "Base Color Intensity")
|
||||
};
|
||||
_baseColorIntensityItem.InitTriggerByChild();
|
||||
|
||||
_alphaAllItem = new ShaderGUISliderItem(rootItem, this)
|
||||
{
|
||||
PropertyName = "_AlphaAll",
|
||||
GuiContent = Content("base.alphaAll", "Overall Alpha"),
|
||||
RangePropertyName = "AlphaAllRangeVec"
|
||||
};
|
||||
_alphaAllItem.InitTriggerByChild();
|
||||
|
||||
_colorAdjustmentBlock = new BlockItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_BaseColorAdjustmentFoldOut",
|
||||
() => Content("base.colorAdjustment", "Color Adjustment"));
|
||||
|
||||
_colorAdjustmentOnlyMainTexItem = new ToggleItem(
|
||||
rootItem,
|
||||
_colorAdjustmentBlock,
|
||||
"_ColorAdjustmentOnlyAffectMainTex",
|
||||
() => Content("base.colorAdjustment.onlyMainTex", "Only Affect Main Texture"),
|
||||
enabled => rootItem.SyncService.ApplyToggleFlag(
|
||||
NBShaderFlags.FLAG_BIT_PARTICLE_COLOR_ADJUSTMENT_ONLY_AFFECT_MAINTEX,
|
||||
enabled));
|
||||
|
||||
_hueShiftBlock = new PropertyToggleBlockItem(
|
||||
rootItem,
|
||||
_colorAdjustmentBlock,
|
||||
"_HueShiftFoldOut",
|
||||
"_HueShift_Toggle",
|
||||
() => Content("base.hueShift", "Hue Shift"),
|
||||
NBShaderFlags.FLAG_BIT_HUESHIFT_ON);
|
||||
_hueShiftSlider = new ShaderGUISliderItem(rootItem, _hueShiftBlock)
|
||||
{
|
||||
PropertyName = "_HueShift",
|
||||
GuiContent = Content("base.hueShift.value", "Hue"),
|
||||
Min = 0f,
|
||||
Max = 1f
|
||||
};
|
||||
_hueShiftSlider.InitTriggerByChild();
|
||||
_hueShiftCustomDataItem = new CustomDataSelectItem(
|
||||
rootItem,
|
||||
_hueShiftBlock,
|
||||
NBShaderFlags.FLAGBIT_POS_0_CUSTOMDATA_HUESHIFT,
|
||||
0,
|
||||
() => Content("base.hueShift.customData", "Hue Custom Data"),
|
||||
IsParticleMode);
|
||||
|
||||
_saturabilityBlock = new PropertyToggleBlockItem(
|
||||
rootItem,
|
||||
_colorAdjustmentBlock,
|
||||
"_SaturabilityFoldOut",
|
||||
"_ChangeSaturability_Toggle",
|
||||
() => Content("base.saturability", "Saturation"),
|
||||
NBShaderFlags.FLAG_BIT_SATURABILITY_ON);
|
||||
_saturabilitySlider = new ShaderGUISliderItem(rootItem, _saturabilityBlock)
|
||||
{
|
||||
PropertyName = "_Saturability",
|
||||
GuiContent = Content("base.saturability.value", "Saturation"),
|
||||
RangePropertyName = "SaturabilityRangeVec"
|
||||
};
|
||||
_saturabilitySlider.InitTriggerByChild();
|
||||
_saturabilityCustomDataItem = new CustomDataSelectItem(
|
||||
rootItem,
|
||||
_saturabilityBlock,
|
||||
NBShaderFlags.FLAGBIT_POS_1_CUSTOMDATA_SATURATE,
|
||||
1,
|
||||
() => Content("base.saturability.customData", "Saturation Custom Data"),
|
||||
IsParticleMode);
|
||||
|
||||
_contrastBlock = new PropertyToggleBlockItem(
|
||||
rootItem,
|
||||
_colorAdjustmentBlock,
|
||||
"_ContrastFoldOut",
|
||||
"_Contrast_Toggle",
|
||||
() => Content("base.contrast", "Contrast"),
|
||||
NBShaderFlags.FLAG_BIT_PARTICLE_1_MAINTEX_CONTRAST,
|
||||
1);
|
||||
_contrastMidColorItem = new ColorItem(rootItem, _contrastBlock, "_ContrastMidColor", () => Content("base.contrast.mid", "Contrast Mid Color"));
|
||||
_contrastSlider = new ShaderGUISliderItem(rootItem, _contrastBlock)
|
||||
{
|
||||
PropertyName = "_Contrast",
|
||||
GuiContent = Content("base.contrast.value", "Contrast"),
|
||||
Min = 0f,
|
||||
Max = 5f
|
||||
};
|
||||
_contrastSlider.InitTriggerByChild();
|
||||
_contrastCustomDataItem = new CustomDataSelectItem(
|
||||
rootItem,
|
||||
_contrastBlock,
|
||||
NBShaderFlags.FLAGBIT_POS_2_CUSTOMDATA_MAINTEX_CONTRAST,
|
||||
2,
|
||||
() => Content("base.contrast.customData", "Contrast Custom Data"),
|
||||
IsParticleMode);
|
||||
|
||||
_baseMapColorRefineBlock = new PropertyToggleBlockItem(
|
||||
rootItem,
|
||||
_colorAdjustmentBlock,
|
||||
"_BaseMapColorRefineFoldOut",
|
||||
"_BaseMapColorRefine_Toggle",
|
||||
() => Content("base.colorRefine", "Color Refine"),
|
||||
NBShaderFlags.FLAG_BIT_PARTICLE_1_MAINTEX_COLOR_REFINE,
|
||||
1);
|
||||
_baseMapColorRefineA = new VectorComponentItem(rootItem, _baseMapColorRefineBlock, "_BaseMapColorRefine", 0, () => Content("base.colorRefine.a", "A Main Color Multiply"), false);
|
||||
_baseMapColorRefineBPower = new VectorComponentItem(rootItem, _baseMapColorRefineBlock, "_BaseMapColorRefine", 1, () => Content("base.colorRefine.bPower", "B Main Color Power"), false);
|
||||
_baseMapColorRefineBMultiply = new VectorComponentItem(rootItem, _baseMapColorRefineBlock, "_BaseMapColorRefine", 2, () => Content("base.colorRefine.bMultiply", "B After Power Multiply"), false);
|
||||
_baseMapColorRefineLerp = new VectorComponentItem(rootItem, _baseMapColorRefineBlock, "_BaseMapColorRefine", 3, () => Content("base.colorRefine.lerp", "A/B Lerp"), true, 0f, 1f);
|
||||
|
||||
_colorMultiAlphaItem = new ToggleItem(
|
||||
rootItem,
|
||||
_colorAdjustmentBlock,
|
||||
"_ColorMultiAlpha",
|
||||
() => Content("base.colorMultiAlpha", "Color Multiply Alpha"),
|
||||
enabled => rootItem.SyncService.ApplyToggleFlag(NBShaderFlags.FLAG_BIT_PARTICLE_COLOR_MULTI_ALPHA, enabled));
|
||||
|
||||
_zTestItem = new ZTestItem(rootItem, this);
|
||||
_cullItem = new CullModeItem(rootItem, this);
|
||||
|
||||
_backFirstPassItem = new ToggleItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_BackFirstPassToggle",
|
||||
() => Content("base.backFirstPass", "Back First Pass"),
|
||||
OnBackFirstPassChanged,
|
||||
() => Is3DTransparent());
|
||||
|
||||
_forceZWriteItem = new ForceZWriteItem(rootItem, this);
|
||||
_affectsShadowsItem = new ToggleItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_AffectsShadows",
|
||||
() => Content("base.affectsShadows", "Affects Shadows"),
|
||||
_ => rootItem.SyncService.SyncMaterialState(),
|
||||
Is3DMode);
|
||||
_transparentShadowDitherItem = new ToggleItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_TransparentShadowDitherToggle",
|
||||
() => Content("base.transparentShadowDither", "Transparent Dither Shadows"),
|
||||
_ => rootItem.SyncService.SyncMaterialState(),
|
||||
ShouldDrawTransparentShadowDither);
|
||||
|
||||
_baseBackColorBlock = new PropertyToggleBlockItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_BaseBackColorFoldOut",
|
||||
"_BaseBackColor_Toggle",
|
||||
() => Content("base.backColor", "Back Color"),
|
||||
NBShaderFlags.FLAG_BIT_PARTICLE_BACKCOLOR,
|
||||
0,
|
||||
isVisible: Is3DMode);
|
||||
_baseBackColorItem = new ColorItem(rootItem, _baseBackColorBlock, "_BaseBackColor", () => Content("base.backColor.color", "Back Color"));
|
||||
|
||||
_distanceFadeBlock = new PropertyToggleBlockItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_DistanceFadeFoldOut",
|
||||
"_DistanceFade_Toggle",
|
||||
() => Content("base.distanceFade", "Distance Fade"),
|
||||
keyword: "_DISTANCE_FADE",
|
||||
isVisible: Is3DMode);
|
||||
_fadeRangeItem = new Vector2LineItem(rootItem, _distanceFadeBlock, "_Fade", true, () => Content("base.distanceFade.range", "Fade Range"));
|
||||
|
||||
_softParticlesBlock = new PropertyToggleBlockItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_SoftParticlesFoldOut",
|
||||
"_SoftParticlesEnabled",
|
||||
() => Content("base.softParticles", "Soft Particles"),
|
||||
keyword: "_SOFTPARTICLES_ON",
|
||||
isVisible: Is3DMode);
|
||||
_softParticleFadeItem = new Vector2LineItem(rootItem, _softParticlesBlock, "_SoftParticleFadeParams", true, () => Content("base.softParticles.range", "Near/Far Fade"));
|
||||
|
||||
_stencilWithoutPlayerItem = new NBShaderKeywordToggleItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_StencilWithoutPlayerToggle",
|
||||
"_STENCIL_WITHOUT_PLAYER",
|
||||
() => Content("base.stencilWithoutPlayer", "Stencil Without Player"),
|
||||
OnStencilWithoutPlayerChanged,
|
||||
Is3DMode);
|
||||
|
||||
_ignoreVertexColorItem = new ToggleItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_IgnoreVetexColor_Toggle",
|
||||
() => Content("base.ignoreVertexColor", "Ignore Vertex Color"),
|
||||
enabled => rootItem.SyncService.ApplyToggleFlag(NBShaderFlags.FLAG_BIT_PARTICLE_1_IGNORE_VERTEX_COLOR, enabled, 1),
|
||||
Is3DMode);
|
||||
|
||||
_fogIntensityItem = new ShaderGUISliderItem(rootItem, this)
|
||||
{
|
||||
PropertyName = "_fogintensity",
|
||||
GuiContent = Content("base.fogIntensity", "Fog Intensity"),
|
||||
Min = 0f,
|
||||
Max = 1f
|
||||
};
|
||||
_fogIntensityItem.InitTriggerByChild();
|
||||
|
||||
InitTriggerByChild();
|
||||
}
|
||||
|
||||
public override void DrawBlock()
|
||||
{
|
||||
_baseColorIntensityItem.OnGUI();
|
||||
_alphaAllItem.OnGUI();
|
||||
_colorAdjustmentBlock.OnGUI();
|
||||
_zTestItem.OnGUI();
|
||||
_cullItem.OnGUI();
|
||||
_backFirstPassItem.OnGUI();
|
||||
DrawBackFirstPassWarning();
|
||||
_forceZWriteItem.OnGUI();
|
||||
_affectsShadowsItem.OnGUI();
|
||||
_transparentShadowDitherItem.OnGUI();
|
||||
_baseBackColorBlock.OnGUI();
|
||||
_distanceFadeBlock.OnGUI();
|
||||
_softParticlesBlock.OnGUI();
|
||||
_stencilWithoutPlayerItem.OnGUI();
|
||||
_ignoreVertexColorItem.OnGUI();
|
||||
|
||||
if (Is3DMode())
|
||||
{
|
||||
_fogIntensityItem.OnGUI();
|
||||
}
|
||||
else if (_nbRootItem.PropertyInfoDic.ContainsKey("_fogintensity"))
|
||||
{
|
||||
_nbRootItem.PropertyInfoDic["_fogintensity"].Property.floatValue = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
private bool Is3DMode()
|
||||
{
|
||||
return _nbRootItem.Context.UIEffectEnabled == MixedBool.False;
|
||||
}
|
||||
|
||||
private bool Is3DTransparent()
|
||||
{
|
||||
return Is3DMode() && _nbRootItem.Context.TransparentMode == TransparentMode.Transparent;
|
||||
}
|
||||
|
||||
private bool IsParticleMode()
|
||||
{
|
||||
return _nbRootItem.Context.ParticleMode == MixedBool.True;
|
||||
}
|
||||
|
||||
private bool ShouldDrawTransparentShadowDither()
|
||||
{
|
||||
return Is3DTransparent() &&
|
||||
_nbRootItem.PropertyInfoDic.TryGetValue("_AffectsShadows", out ShaderPropertyInfo info) &&
|
||||
!info.Property.hasMixedValue &&
|
||||
info.Property.floatValue > 0.5f;
|
||||
}
|
||||
|
||||
private void DrawBackFirstPassWarning()
|
||||
{
|
||||
if (!Is3DTransparent() ||
|
||||
!_nbRootItem.PropertyInfoDic.ContainsKey("_BackFirstPassToggle") ||
|
||||
_nbRootItem.PropertyInfoDic["_BackFirstPassToggle"].Property.hasMixedValue ||
|
||||
_nbRootItem.PropertyInfoDic["_BackFirstPassToggle"].Property.floatValue <= 0.5f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DrawLayoutHelpBox(
|
||||
NBShaderInspectorLocalization.GetInspectorText(
|
||||
"base.backFirstPass.warning",
|
||||
"预渲染反面会导致打断动态合批,请谨慎使用。"),
|
||||
MessageType.Warning);
|
||||
}
|
||||
|
||||
private void OnBackFirstPassChanged(bool enabled)
|
||||
{
|
||||
_nbRootItem.SyncService.ApplyShaderPass("SRPDefaultUnlit", enabled);
|
||||
if (enabled && _nbRootItem.PropertyInfoDic.ContainsKey("_Cull"))
|
||||
{
|
||||
_nbRootItem.PropertyInfoDic["_Cull"].Property.floatValue = (float)RenderFace.Front;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnStencilWithoutPlayerChanged(bool enabled)
|
||||
{
|
||||
_nbRootItem.SyncService.ApplyStencilPreset(enabled ? "ParticleWithoutPlayer" : "ParticleBaseDefault");
|
||||
if (_nbRootItem.PropertyInfoDic.ContainsKey("_CustomStencilTest"))
|
||||
{
|
||||
_nbRootItem.PropertyInfoDic["_CustomStencilTest"].Property.floatValue = enabled ? 1f : 0f;
|
||||
}
|
||||
}
|
||||
|
||||
private static GUIContent Content(string key, string fallback, string tip = "")
|
||||
{
|
||||
return NBShaderInspectorLocalization.MakeInspectorContent(key, fallback, tip);
|
||||
}
|
||||
}
|
||||
|
||||
public class ZTestItem : ShaderGUIPopUpItem
|
||||
{
|
||||
private static readonly string[] Options = Enum.GetNames(typeof(CompareFunction));
|
||||
|
||||
public ZTestItem(ShaderGUIRootItem rootItem, ShaderGUIItem parentItem) : base(rootItem, parentItem: parentItem)
|
||||
{
|
||||
PropertyName = "_ZTest";
|
||||
GuiContent = NBShaderInspectorLocalization.MakeInspectorContent("base.ztest", "ZTest");
|
||||
PopUpNames = NBShaderInspectorLocalization.GetInspectorOptions("base.ztest", Options);
|
||||
InitTriggerByChild();
|
||||
}
|
||||
|
||||
public override void OnGUI()
|
||||
{
|
||||
if (RootItem is NBShaderRootItem nbRootItem)
|
||||
{
|
||||
if (nbRootItem.Context.UIEffectEnabled == MixedBool.True)
|
||||
{
|
||||
if (!Mathf.Approximately(PropertyInfo.Property.floatValue, (float)CompareFunction.LessEqual))
|
||||
{
|
||||
PropertyInfo.Property.floatValue = (float)CompareFunction.LessEqual;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (nbRootItem.Context.UIEffectEnabled == MixedBool.Mixed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
base.OnGUI();
|
||||
}
|
||||
}
|
||||
|
||||
public class CullModeItem : ShaderGUIPopUpItem
|
||||
{
|
||||
private static readonly string[] Options = { "Both", "Back", "Front" };
|
||||
|
||||
public CullModeItem(ShaderGUIRootItem rootItem, ShaderGUIItem parentItem) : base(rootItem, parentItem)
|
||||
{
|
||||
PropertyName = "_Cull";
|
||||
GuiContent = NBShaderInspectorLocalization.MakeInspectorContent("base.cull", "Cull");
|
||||
PopUpNames = NBShaderInspectorLocalization.GetInspectorOptions("base.cull", Options);
|
||||
InitTriggerByChild();
|
||||
}
|
||||
|
||||
public override void OnGUI()
|
||||
{
|
||||
base.OnGUI();
|
||||
}
|
||||
}
|
||||
|
||||
public class ForceZWriteItem : ShaderGUIPopUpItem
|
||||
{
|
||||
private static readonly string[] Options = { "Default", "Force On", "Force Off" };
|
||||
|
||||
public ForceZWriteItem(ShaderGUIRootItem rootItem, ShaderGUIItem parentItem) : base(rootItem, parentItem)
|
||||
{
|
||||
PropertyName = "_ForceZWriteToggle";
|
||||
GuiContent = NBShaderInspectorLocalization.MakeInspectorContent("base.forceZWrite", "Force ZWrite");
|
||||
PopUpNames = NBShaderInspectorLocalization.GetInspectorOptions("base.forceZWrite", Options);
|
||||
InitTriggerByChild();
|
||||
}
|
||||
|
||||
public override void OnGUI()
|
||||
{
|
||||
base.OnGUI();
|
||||
}
|
||||
|
||||
public override void OnEndChange()
|
||||
{
|
||||
base.OnEndChange();
|
||||
if (RootItem is NBShaderRootItem nbRootItem)
|
||||
{
|
||||
nbRootItem.SyncService.SyncMaterialState();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 135aa1fd916c4155b15cada119fdf3fd
|
||||
timeCreated: 1758549245
|
||||
@@ -0,0 +1,136 @@
|
||||
namespace NBShaderEditor
|
||||
{
|
||||
public class FeatureBigBlockItem : BigBlockItem
|
||||
{
|
||||
public FeatureBigBlockItem(NBShaderRootItem rootItem, ShaderGUIItem parentItem)
|
||||
: base(
|
||||
rootItem,
|
||||
parentItem,
|
||||
"_FeatureBigBlockItemFoldOut",
|
||||
() => NBShaderInspectorLocalization.MakeContent(
|
||||
"inspector.block.feature.label",
|
||||
"特效功能",
|
||||
"inspector.block.feature.tip",
|
||||
"遮罩、扭曲、溶解等特效功能"))
|
||||
{
|
||||
new MaskFeatureItem(rootItem, this);
|
||||
new NoiseAndDistortFeatureItem(rootItem, this);
|
||||
new ChromaticAberrationFeatureItem(rootItem, this);
|
||||
new EmissionFeatureItem(rootItem, this);
|
||||
new ColorBlendFeatureItem(rootItem, this);
|
||||
new RampColorFeatureItem(rootItem, this);
|
||||
new DissolveFeatureItem(rootItem, this);
|
||||
new ProgramNoiseFeatureItem(rootItem, this);
|
||||
new SharedUVFeatureItem(rootItem, this);
|
||||
new FresnelFeatureItem(rootItem, this);
|
||||
new VertexOffsetFeatureItem(rootItem, this);
|
||||
new DepthFeatureItem(rootItem, this);
|
||||
new ParallaxFeatureItem(rootItem, this);
|
||||
new PortalFeatureItem(rootItem, this);
|
||||
new FlipbookFeatureItem(rootItem, this);
|
||||
new VatFeatureItem(rootItem, this);
|
||||
|
||||
InitTriggerByChild();
|
||||
}
|
||||
|
||||
public override void DrawBlock()
|
||||
{
|
||||
for (int i = 0; i < ChildrenItemList.Count; i++)
|
||||
{
|
||||
ShaderGUIItem child = ChildrenItemList[i];
|
||||
if (IsFeatureItemAllowed(child))
|
||||
{
|
||||
child.OnGUI();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsFeatureItemAllowed(ShaderGUIItem item)
|
||||
{
|
||||
if (item is DepthFeatureItem)
|
||||
{
|
||||
var rootItem = (NBShaderRootItem)RootItem;
|
||||
return rootItem.Context == null ||
|
||||
rootItem.Context.IsAnyKeywordAllowed("_DEPTH_DECAL", "_DEPTH_OUTLINE");
|
||||
}
|
||||
|
||||
string keyword = GetFeatureKeyword(item);
|
||||
return string.IsNullOrEmpty(keyword) || ((NBShaderRootItem)RootItem).Context.IsKeywordAllowed(keyword);
|
||||
}
|
||||
|
||||
private static string GetFeatureKeyword(ShaderGUIItem item)
|
||||
{
|
||||
if (item is MaskFeatureItem)
|
||||
{
|
||||
return "_MASKMAP_ON";
|
||||
}
|
||||
|
||||
if (item is NoiseAndDistortFeatureItem)
|
||||
{
|
||||
return "_NOISEMAP";
|
||||
}
|
||||
|
||||
if (item is ChromaticAberrationFeatureItem)
|
||||
{
|
||||
return "_CHROMATIC_ABERRATION";
|
||||
}
|
||||
|
||||
if (item is EmissionFeatureItem)
|
||||
{
|
||||
return "_EMISSION";
|
||||
}
|
||||
|
||||
if (item is ColorBlendFeatureItem)
|
||||
{
|
||||
return "_COLORMAPBLEND";
|
||||
}
|
||||
|
||||
if (item is RampColorFeatureItem)
|
||||
{
|
||||
return "_COLOR_RAMP";
|
||||
}
|
||||
|
||||
if (item is DissolveFeatureItem)
|
||||
{
|
||||
return "_DISSOLVE";
|
||||
}
|
||||
|
||||
if (item is ProgramNoiseFeatureItem)
|
||||
{
|
||||
return "_PROGRAM_NOISE";
|
||||
}
|
||||
|
||||
if (item is SharedUVFeatureItem)
|
||||
{
|
||||
return "_SHARED_UV";
|
||||
}
|
||||
|
||||
if (item is FresnelFeatureItem)
|
||||
{
|
||||
return "_FRESNEL";
|
||||
}
|
||||
|
||||
if (item is ParallaxFeatureItem)
|
||||
{
|
||||
return "_PARALLAX_MAPPING";
|
||||
}
|
||||
|
||||
if (item is VatFeatureItem)
|
||||
{
|
||||
return "_VAT";
|
||||
}
|
||||
|
||||
if (item is FlipbookFeatureItem)
|
||||
{
|
||||
return "_FLIPBOOKBLENDING_ON";
|
||||
}
|
||||
|
||||
if (item is VertexOffsetFeatureItem)
|
||||
{
|
||||
return "_VERTEX_OFFSET";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9bfc5d4ee5b1dc64caf81212c9880723
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f682036413e49019b4856f356996a4c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,28 @@
|
||||
using NBShader;
|
||||
|
||||
namespace NBShaderEditor
|
||||
{
|
||||
internal sealed class ChromaticAberrationFeatureItem : FeatureToggleFoldOutItem
|
||||
{
|
||||
public ChromaticAberrationFeatureItem(NBShaderRootItem rootItem, ShaderGUIItem parentItem)
|
||||
: base(
|
||||
rootItem,
|
||||
parentItem,
|
||||
"_ChromaticAberrationFoldOut",
|
||||
"_Distortion_Choraticaberrat_Toggle",
|
||||
"色散",
|
||||
keyword: "_CHROMATIC_ABERRATION")
|
||||
{
|
||||
ShaderGUIItem chromaticNoiseAffect = new NoiseAffectItem(rootItem, this);
|
||||
new ToggleItem(
|
||||
rootItem,
|
||||
chromaticNoiseAffect,
|
||||
"_Distortion_Choraticaberrat_WithNoise_Toggle",
|
||||
() => Content("色散强度受扭曲强度影响"),
|
||||
enabled => rootItem.SyncService.ApplyToggleFlag(NBShaderFlags.FLAG_BIT_PARTICLE_NOISE_CHORATICABERRAT_WITH_NOISE, enabled));
|
||||
new VectorComponentItem(rootItem, this, "_DistortionDirection", 2, () => Content("色散强度"), false);
|
||||
new CustomDataSelectItem(rootItem, this, NBShaderFlags.FLAGBIT_POS_0_CUSTOMDATA_CHORATICABERRAT_INTENSITY, 0, () => Content("色散强度自定义曲线"));
|
||||
InitTriggerByChild();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0e463cf92ee4725b57727544bc58488
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using NBShader;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBShaderEditor
|
||||
{
|
||||
internal sealed class ColorBlendFeatureItem : FeatureToggleFoldOutItem
|
||||
{
|
||||
private static readonly string[] OnOffNames = { "关闭", "开启" };
|
||||
|
||||
public ColorBlendFeatureItem(NBShaderRootItem rootItem, ShaderGUIItem parentItem)
|
||||
: base(rootItem, parentItem, "_ColorBlendBlockFoldOut", "_ColorBlendMap_Toggle", "渐变(颜色相乘)", keyword: "_COLORMAPBLEND")
|
||||
{
|
||||
AddTextureWithWrap(rootItem, this, "_ColorBlendMap", "颜色渐变贴图", NBShaderFlags.FLAG_BIT_WRAPMODE_COLORBLENDMAP, "_ColorBlendColor");
|
||||
new UVModeSelectItem(rootItem, this, "_ColorBlendUVModeFoldOut", NBShaderFlags.FLAG_BIT_UVMODE_POS_0_COLOR_BLEND_MAP, 0, () => Content("颜色渐变贴图UV来源"), "_ColorBlendMap");
|
||||
new CustomDataSelectItem(rootItem, this, NBShaderFlags.FLAGBIT_POS_3_CUSTOMDATA_COLOR_BLEND_OFFSET_X, 3, () => Content("颜色渐变贴图X轴偏移自定义曲线"));
|
||||
new CustomDataSelectItem(rootItem, this, NBShaderFlags.FLAGBIT_POS_3_CUSTOMDATA_COLOR_BLEND_OFFSET_Y, 3, () => Content("颜色渐变贴图Y轴偏移自定义曲线"));
|
||||
new VectorComponentItem(rootItem, this, "_ColorBlendVec", 3, () => Content("颜色渐变贴图旋转"), true, 0f, 360f);
|
||||
new Vector2LineItem(rootItem, this, "_ColorBlendMapOffset", true, () => Content("颜色渐变贴图偏移速度"));
|
||||
ShaderGUIItem colorBlendNoiseAffect = new NoiseAffectItem(rootItem, this);
|
||||
new VectorComponentItem(rootItem, colorBlendNoiseAffect, "_ColorBlendVec", 0, () => Content("颜色渐变扭曲强度"), true);
|
||||
new FeaturePopupItem(rootItem, this, "_ColorBlendAlphaMultiplyMode", () => Content("颜色渐变图Alpha作用"), OnOffNames,
|
||||
property => rootItem.SyncService.ApplyToggleFlag(NBShaderFlags.FLAG_BIT_PARTICLE_COLOR_BLEND_ALPHA_MULTIPLY_MODE, property.floatValue > 0.5f));
|
||||
new VectorComponentItem(rootItem, this, "_ColorBlendVec", 2, () => Content("颜色渐变图Alpha强度"), true);
|
||||
InitTriggerByChild();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a4e1e4d5240a4b748b50807573e22ab8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using NBShader;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBShaderEditor
|
||||
{
|
||||
internal sealed class DepthFeatureItem : ShaderGUIItem
|
||||
{
|
||||
public DepthFeatureItem(NBShaderRootItem rootItem, ShaderGUIItem parentItem)
|
||||
: base(rootItem, parentItem)
|
||||
{
|
||||
new DepthOutlineFeatureItem(rootItem, this);
|
||||
new ToggleItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_DepthDecal_Toggle",
|
||||
() => FeatureToggleFoldOutItem.Content("深度贴花"),
|
||||
rootItem.SyncService.ApplyDepthDecalEnabled,
|
||||
FeatureToggleFoldOutItem.TierVisible(
|
||||
rootItem,
|
||||
"_DEPTH_DECAL",
|
||||
() => rootItem.Context.UIEffectEnabled != MixedBool.True));
|
||||
InitTriggerByChild();
|
||||
}
|
||||
|
||||
public override void OnGUI()
|
||||
{
|
||||
for (int i = 0; i < ChildrenItemList.Count; i++)
|
||||
{
|
||||
ChildrenItemList[i].OnGUI();
|
||||
}
|
||||
}
|
||||
|
||||
public override void CheckIsPropertyModified(bool isCallByChild = false)
|
||||
{
|
||||
HasModified = false;
|
||||
for (int i = 0; i < ChildrenItemList.Count; i++)
|
||||
{
|
||||
HasModified |= ChildrenItemList[i].HasModified;
|
||||
}
|
||||
|
||||
ParentItem?.CheckIsPropertyModified(true);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class DepthOutlineFeatureItem : FeatureToggleFoldOutItem
|
||||
{
|
||||
public DepthOutlineFeatureItem(NBShaderRootItem rootItem, ShaderGUIItem parentItem)
|
||||
: base(
|
||||
rootItem,
|
||||
parentItem,
|
||||
"_DepthOutlineBlockFoldOut",
|
||||
"_DepthOutline_Toggle",
|
||||
"深度描边",
|
||||
keyword: "_DEPTH_OUTLINE",
|
||||
isVisible: () => rootItem.Context.UIEffectEnabled != MixedBool.True)
|
||||
{
|
||||
new ColorItem(rootItem, this, "_DepthOutline_Color", () => Content("深度描边颜色"));
|
||||
new Vector2LineItem(rootItem, this, "_DepthOutline_Vec", true, () => Content("深度描边距离"));
|
||||
InitTriggerByChild();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0482c5f68e7b4438bc494df4ebf47267
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using NBShader;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBShaderEditor
|
||||
{
|
||||
internal sealed class DissolveFeatureItem : FeatureToggleFoldOutItem
|
||||
{
|
||||
private static readonly string[] RampSourceNames = { "渐变", "贴图" };
|
||||
private static readonly string[] BlendModeNames = { "叠加", "相乘" };
|
||||
private static readonly string[] DissolveMaskModeNames = { "Process Dissolve", "Dissolve Mask" };
|
||||
|
||||
public DissolveFeatureItem(NBShaderRootItem rootItem, ShaderGUIItem parentItem)
|
||||
: base(rootItem, parentItem, "_DissolveBlockFoldOut", "_Dissolve_Toggle", "溶解", keyword: "_DISSOLVE")
|
||||
{
|
||||
new NBShaderKeywordToggleItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_NB_Debug_Dissolve",
|
||||
"NB_DEBUG_DISSOLVE",
|
||||
() => Content("溶解度黑白值测试"),
|
||||
isVisible: null);
|
||||
TextureRelatedFoldOutItem dissolveMapRelatedFoldOut = AddTextureWithRelatedFoldOut(rootItem, this, "_DissolveMap", "溶解贴图", "_DissolveMapFoldOut", NBShaderFlags.FLAG_BIT_WRAPMODE_DISSOLVE_MAP);
|
||||
new ColorChannelSelectItem(rootItem, dissolveMapRelatedFoldOut, NBShaderFlags.FLAG_BIT_COLOR_CHANNEL_POS_0_DISSOLVE_MAP, 0, () => Content("溶解贴图通道选择"));
|
||||
new CustomDataSelectItem(rootItem, dissolveMapRelatedFoldOut, NBShaderFlags.FLAGBIT_POS_1_CUSTOMDATA_DISSOLVE_OFFSET_X, 1, () => Content("溶解贴图X轴偏移自定义曲线"));
|
||||
new CustomDataSelectItem(rootItem, dissolveMapRelatedFoldOut, NBShaderFlags.FLAGBIT_POS_1_CUSTOMDATA_DISSOLVE_OFFSET_Y, 1, () => Content("溶解贴图Y轴偏移自定义曲线"));
|
||||
new UVModeSelectItem(rootItem, dissolveMapRelatedFoldOut, "_DissolveUVModeFoldOut", NBShaderFlags.FLAG_BIT_UVMODE_POS_0_DISSOLVE_MAP, 0, () => Content("溶解贴图UV来源"), "_DissolveMap");
|
||||
new Vector2LineItem(rootItem, dissolveMapRelatedFoldOut, "_DissolveOffsetRotateDistort", true, () => Content("溶解贴图偏移速度"));
|
||||
new VectorComponentItem(rootItem, dissolveMapRelatedFoldOut, "_DissolveOffsetRotateDistort", 2, () => Content("溶解贴图旋转"), true, 0f, 360f);
|
||||
new PNoiseBlendModeItem(rootItem, this, NBShaderFlags.FLAG_BIT_PNOISE_BLEND_POS_0_DISSOLVE, "_DissolvePNoiseBlendOpacity", () => Content("溶解程序噪波混合"),
|
||||
() => rootItem.Context.ProgramNoiseEnabled == MixedBool.True);
|
||||
new VectorComponentItem(rootItem, this, "_Dissolve", 1, () => Content("溶解值Pow"), true, 0.001f, 10f);
|
||||
new VectorComponentRangeSliderItem(rootItem, this, "_Dissolve", 0, "DissolveXRangeVec", () => Content("溶解强度"));
|
||||
new CustomDataSelectItem(rootItem, this, NBShaderFlags.FLAGBIT_POS_0_CUSTOMDATA_DISSOLVE_INTENSITY, 0, () => Content("溶解强度自定义曲线"));
|
||||
new VectorComponentItem(rootItem, this, "_Dissolve", 3, () => Content("溶解硬软度"), true, 0.001f, 1f);
|
||||
ShaderGUIItem dissolveNoiseAffect = new NoiseAffectItem(rootItem, this);
|
||||
new VectorComponentItem(rootItem, dissolveNoiseAffect, "_DissolveOffsetRotateDistort", 3, () => Content("溶解贴图扭曲强度"), false);
|
||||
|
||||
PropertyToggleBlockItem lineBlock = ToggleBlock(rootItem, "_DissolveLineFoldOut", "_DissolveLineMaskToggle", "溶解描边",
|
||||
NBShaderFlags.FLAG_BIT_PARTICLE_1_DISSOLVE_LINE_MASK, 1, parent: this);
|
||||
new ColorItem(rootItem, lineBlock, "_DissolveLineColor", () => Content("溶解描边颜色"));
|
||||
new VectorComponentRangeSliderItem(rootItem, lineBlock, "_Dissolve_Vec2", 0, "Dissolve2XRangeVec", () => Content("描边位置"));
|
||||
new VectorComponentRangeSliderItem(rootItem, lineBlock, "_Dissolve_Vec2", 1, "Dissolve2YRangeVec", () => Content("描边软硬"));
|
||||
|
||||
PropertyToggleBlockItem rampBlock = ToggleBlock(rootItem, "_DissolveRampFoldOut", "_Dissolve_useRampMap_Toggle", "溶解Ramp图功能",
|
||||
parent: this, keyword: "_DISSOLVE_RAMP",
|
||||
onValueChanged: _ => rootItem.SyncService.SyncMaterialState());
|
||||
Func<bool> isDissolveRampMapVisible = TierVisible(rootItem, "_DISSOLVE_RAMP_MAP", () => IsPropertyMode(rootItem, "_DissolveRampSourceMode", 1));
|
||||
new FeaturePopupItem(rootItem, rampBlock, "_DissolveRampSourceMode", () => Content("溶解Ramp模式"), RampSourceNames,
|
||||
_ => rootItem.SyncService.SyncMaterialState(),
|
||||
keyword: "_DISSOLVE_RAMP_MAP");
|
||||
AddTextureWithWrap(rootItem, rampBlock, "_DissolveRampMap", "溶解Ramp图", NBShaderFlags.FLAG_BIT_WRAPMODE_DISSOLVE_RAMPMAP, "_DissolveRampColor",
|
||||
isDissolveRampMapVisible);
|
||||
AddGradient(rootItem, rampBlock, "Ramp颜色", "_DissolveRampCount", "_DissolveRampColor", "_DissolveRampAlpha", hdr: true,
|
||||
isVisible: () => IsPropertyMode(rootItem, "_DissolveRampSourceMode", 0));
|
||||
new TextureScaleOffsetItem(rootItem, rampBlock, "_DissolveRampMap", false, () => IsPropertyMode(rootItem, "_DissolveRampSourceMode", 0), TillingContent, OffsetContent);
|
||||
new ColorItem(rootItem, rampBlock, "_DissolveRampColor", () => Content("Ramp颜色叠加"), () => IsPropertyMode(rootItem, "_DissolveRampSourceMode", 0));
|
||||
new WrapModeItem(rootItem, rampBlock, NBShaderFlags.FLAG_BIT_WRAPMODE_DISSOLVE_RAMPMAP, () => Content("溶解RampUV Wrap"), 2,
|
||||
() => IsPropertyMode(rootItem, "_DissolveRampSourceMode", 0));
|
||||
new FeaturePopupItem(rootItem, rampBlock, "_DissolveRampColorBlendMode", () => Content("溶解Ramp混合模式"), BlendModeNames,
|
||||
property => rootItem.SyncService.ApplyToggleFlag(NBShaderFlags.FLAG_BIT_PARTICLE_1_DISSOLVE_RAMP_MULITPLY, property.floatValue > 0.5f, 1));
|
||||
|
||||
PropertyToggleBlockItem maskBlock = ToggleBlock(rootItem, "_DissolveMaskFoldOut", "_DissolveMask_Toggle", "溶解遮罩图(过程溶解)",
|
||||
parent: this, keyword: "_DISSOLVE_MASK");
|
||||
new FeaturePopupItem(rootItem, maskBlock, "_DissolveMaskMode", () => Content("溶解遮罩模式"), DissolveMaskModeNames);
|
||||
AddTextureWithWrap(rootItem, maskBlock, "_DissolveMaskMap", "溶解遮罩图", NBShaderFlags.FLAG_BIT_WRAPMODE_DISSOLVE_MASKMAP);
|
||||
new UVModeSelectItem(rootItem, maskBlock, "_DissolveMaskUVModeFoldOut", NBShaderFlags.FLAG_BIT_UVMODE_POS_0_DISSOLVE_MASK_MAP, 0, () => Content("溶解遮罩图UV来源"), "_DissolveMaskMap");
|
||||
new ColorChannelSelectItem(rootItem, maskBlock, NBShaderFlags.FLAG_BIT_COLOR_CHANNEL_POS_0_DISSOLVE_MASK_MAP, 0, () => Content("溶解遮罩图通道选择"));
|
||||
new VectorComponentItem(rootItem, maskBlock, "_Dissolve", 2, () => Content("溶解遮罩强度"), false, isVisible: () => !IsDissolveMaskStrengthSlider(rootItem));
|
||||
new VectorComponentItem(rootItem, maskBlock, "_Dissolve", 2, () => Content("溶解遮罩强度"), true, 0f, 2f, () => IsDissolveMaskStrengthSlider(rootItem));
|
||||
new CustomDataSelectItem(rootItem, maskBlock, NBShaderFlags.FLAGBIT_POS_1_CUSTOMDATA_DISSOLVE_MASK_INTENSITY, 1, () => Content("溶解遮罩图强度自定义曲线"));
|
||||
InitTriggerByChild();
|
||||
}
|
||||
|
||||
private static bool IsDissolveMaskStrengthSlider(NBShaderRootItem rootItem)
|
||||
{
|
||||
return rootItem.PropertyInfoDic.TryGetValue("_DissolveMaskMode", out ShaderPropertyInfo info) &&
|
||||
!info.Property.hasMixedValue &&
|
||||
info.Property.floatValue > 0.5f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 64cae13520d842939c7d27d6ffadd01d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using NBShader;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBShaderEditor
|
||||
{
|
||||
internal sealed class EmissionFeatureItem : FeatureToggleFoldOutItem
|
||||
{
|
||||
public EmissionFeatureItem(NBShaderRootItem rootItem, ShaderGUIItem parentItem)
|
||||
: base(rootItem, parentItem, "_EmissionBlockFoldOut", "_EmissionEnabled", "流光(颜色相加)", keyword: "_EMISSION")
|
||||
{
|
||||
AddTextureWithWrap(rootItem, this, "_EmissionMap", "流光贴图", NBShaderFlags.FLAG_BIT_WRAPMODE_EMISSIONMAP, "_EmissionMapColor");
|
||||
new UVModeSelectItem(rootItem, this, "_EmissionUVModeFoldOut", NBShaderFlags.FLAG_BIT_UVMODE_POS_0_EMISSION_MAP, 0, () => Content("流光贴图UV来源"), "_EmissionMap");
|
||||
new CustomDataSelectItem(rootItem, this, NBShaderFlags.FLAGBIT_POS_3_CUSTOMDATA_EMISSION_OFFSET_X, 3, () => Content("流光贴图X轴偏移自定义曲线"));
|
||||
new CustomDataSelectItem(rootItem, this, NBShaderFlags.FLAGBIT_POS_3_CUSTOMDATA_EMISSION_OFFSET_Y, 3, () => Content("流光贴图Y轴偏移自定义曲线"));
|
||||
ShaderGUISliderItem emissionMapUVRotationItem = new ShaderGUISliderItem(rootItem, this)
|
||||
{
|
||||
PropertyName = "_EmissionMapUVRotation",
|
||||
GuiContent = Content("流光贴图旋转"),
|
||||
Min = 0f,
|
||||
Max = 360f
|
||||
};
|
||||
emissionMapUVRotationItem.InitTriggerByChild();
|
||||
new Vector2LineItem(rootItem, this, "_EmissionMapUVOffset", true, () => Content("流光贴图偏移速度"));
|
||||
ShaderGUIItem emissionNoiseAffect = new NoiseAffectItem(rootItem, this);
|
||||
ShaderGUIFloatItem emissionDistortionIntensityItem = new ShaderGUIFloatItem(rootItem, emissionNoiseAffect)
|
||||
{
|
||||
PropertyName = "_Emi_Distortion_intensity",
|
||||
GuiContent = Content("流光贴图扭曲强度")
|
||||
};
|
||||
emissionDistortionIntensityItem.InitTriggerByChild();
|
||||
ShaderGUIFloatItem emissionMapColorIntensityItem = new ShaderGUIFloatItem(rootItem, this)
|
||||
{
|
||||
PropertyName = "_EmissionMapColorIntensity",
|
||||
GuiContent = Content("流光颜色强度")
|
||||
};
|
||||
emissionMapColorIntensityItem.InitTriggerByChild();
|
||||
InitTriggerByChild();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ed7051253ed04c968aa25e96aa0e5d5d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,175 @@
|
||||
using System;
|
||||
using NBShader;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBShaderEditor
|
||||
{
|
||||
internal sealed class NoiseAffectItem : ShaderGUIItem
|
||||
{
|
||||
private readonly NBShaderRootItem _nbRootItem;
|
||||
|
||||
public NoiseAffectItem(NBShaderRootItem rootItem, ShaderGUIItem parentItem)
|
||||
: base(rootItem, parentItem)
|
||||
{
|
||||
_nbRootItem = rootItem;
|
||||
}
|
||||
|
||||
public override void OnGUI()
|
||||
{
|
||||
bool previousMixedValue = EditorGUI.showMixedValue;
|
||||
bool noiseEnabledHasMixedValue = _nbRootItem.Context.NoiseEnabled == MixedBool.Mixed;
|
||||
using (new InheritedControlDisabledScope(_nbRootItem.Context.NoiseEnabled == MixedBool.False))
|
||||
{
|
||||
for (int i = 0; i < ChildrenItemList.Count; i++)
|
||||
{
|
||||
EditorGUI.showMixedValue = noiseEnabledHasMixedValue;
|
||||
ChildrenItemList[i].OnGUI();
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUI.showMixedValue = previousMixedValue;
|
||||
}
|
||||
|
||||
public override void CheckIsPropertyModified(bool isCallByChild = false)
|
||||
{
|
||||
HasModified = false;
|
||||
for (int i = 0; i < ChildrenItemList.Count; i++)
|
||||
{
|
||||
HasModified |= ChildrenItemList[i].HasModified;
|
||||
}
|
||||
|
||||
ParentItem?.CheckIsPropertyModified(true);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class FlipbookFeatureItem : ToggleItem
|
||||
{
|
||||
private readonly NBShaderRootItem _nbRootItem;
|
||||
|
||||
public FlipbookFeatureItem(NBShaderRootItem rootItem, ShaderGUIItem parentItem)
|
||||
: base(
|
||||
rootItem,
|
||||
parentItem,
|
||||
"_FlipbookBlending",
|
||||
() => FeatureToggleFoldOutItem.Content("序列帧融帧(丝滑)"),
|
||||
rootItem.SyncService.ApplyFlipbookEnabled)
|
||||
{
|
||||
_nbRootItem = rootItem;
|
||||
}
|
||||
|
||||
public override void DrawBlock()
|
||||
{
|
||||
if (PropertyInfo.Property.hasMixedValue || PropertyInfo.Property.floatValue <= 0.5f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_nbRootItem.Context.MeshSourceMode == MeshSourceMode.Particle ||
|
||||
_nbRootItem.Context.MeshSourceMode == MeshSourceMode.UIParticle)
|
||||
{
|
||||
if (HasSpecialUVChannel())
|
||||
{
|
||||
using (ParentControlDisabledScope())
|
||||
{
|
||||
DrawLayoutHelpBox(
|
||||
FeatureToggleFoldOutItem.Text(
|
||||
"feature.flipbook.specialUvWarning.message",
|
||||
"序列帧融帧和特殊UV通道同时开启,粒子序列帧应该影响UV0和UV1两个通道,特殊通道只能使用UV3(原始UV)"),
|
||||
MessageType.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (ParentControlDisabledScope())
|
||||
{
|
||||
DrawLayoutHelpBox(
|
||||
FeatureToggleFoldOutItem.Text(
|
||||
"feature.flipbook.particleInfo.message",
|
||||
"AnimationSheet的AffectUVChannel需要有UV0和UV1"),
|
||||
MessageType.Info);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (_nbRootItem.Context.MeshSourceMode == MeshSourceMode.Mesh)
|
||||
{
|
||||
using (ParentControlDisabledScope())
|
||||
{
|
||||
DrawLayoutHelpBox(
|
||||
FeatureToggleFoldOutItem.Text(
|
||||
"feature.flipbook.meshInfo.message",
|
||||
"需要添加AnimationSheetHelper脚本"),
|
||||
MessageType.Info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool HasSpecialUVChannel()
|
||||
{
|
||||
for (int i = 0; i < _nbRootItem.ShaderFlags.Count; i++)
|
||||
{
|
||||
if (_nbRootItem.ShaderFlags[i] is NBShaderFlags flags &&
|
||||
flags.CheckIsUVModeOn(NBShaderFlags.UVMode.SpecialUVChannel))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class VatFrameCustomDataItem : CustomDataSelectItem
|
||||
{
|
||||
private readonly Func<bool> _isVisible;
|
||||
private readonly Func<bool> _anyVatFrameCustomDataVisible;
|
||||
|
||||
public VatFrameCustomDataItem(
|
||||
NBShaderRootItem rootItem,
|
||||
ShaderGUIItem parentItem,
|
||||
Func<GUIContent> contentProvider,
|
||||
Func<bool> isVisible,
|
||||
Func<bool> anyVatFrameCustomDataVisible)
|
||||
: base(rootItem, parentItem, NBShaderFlags.FLAGBIT_POS_2_CUSTOMDATA_VAT_FRAME, 2, contentProvider)
|
||||
{
|
||||
_isVisible = isVisible;
|
||||
_anyVatFrameCustomDataVisible = anyVatFrameCustomDataVisible;
|
||||
}
|
||||
|
||||
public override void OnGUI()
|
||||
{
|
||||
if (_isVisible != null && !_isVisible())
|
||||
{
|
||||
if (_anyVatFrameCustomDataVisible == null || !_anyVatFrameCustomDataVisible())
|
||||
{
|
||||
ClearVatFrameCustomData();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
base.OnGUI();
|
||||
}
|
||||
|
||||
private void ClearVatFrameCustomData()
|
||||
{
|
||||
for (int i = 0; i < RootItem.ShaderFlags.Count; i++)
|
||||
{
|
||||
if (RootItem.ShaderFlags[i] is NBShaderFlags flags && flags.material != null)
|
||||
{
|
||||
if (flags.GetCustomDataFlag(NBShaderFlags.FLAGBIT_POS_2_CUSTOMDATA_VAT_FRAME, 2) !=
|
||||
NBShaderFlags.CutomDataComponent.Off)
|
||||
{
|
||||
flags.SetCustomDataFlag(
|
||||
NBShaderFlags.CutomDataComponent.Off,
|
||||
NBShaderFlags.FLAGBIT_POS_2_CUSTOMDATA_VAT_FRAME,
|
||||
2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c8170a5849c4a5abf332cdf3832fa02
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,276 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NBShader;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBShaderEditor
|
||||
{
|
||||
internal abstract class FeatureToggleFoldOutItem : PropertyToggleBlockItem
|
||||
{
|
||||
private const string LocalizationTableName = NBShaderInspectorLocalization.TableName;
|
||||
private static readonly Dictionary<string, GUIContent> ContentCache =
|
||||
new Dictionary<string, GUIContent>(StringComparer.Ordinal);
|
||||
private static readonly Dictionary<string, string[]> PopupOptionsCache =
|
||||
new Dictionary<string, string[]>(StringComparer.Ordinal);
|
||||
private static string s_CacheLanguage;
|
||||
|
||||
protected FeatureToggleFoldOutItem(
|
||||
NBShaderRootItem rootItem,
|
||||
ShaderGUIItem parentItem,
|
||||
string foldOutPropertyName,
|
||||
string togglePropertyName,
|
||||
string label,
|
||||
int flagBits = 0,
|
||||
int flagIndex = 0,
|
||||
string keyword = null,
|
||||
Action<bool> onValueChanged = null,
|
||||
Func<bool> isVisible = null,
|
||||
bool bold = true)
|
||||
: base(
|
||||
rootItem,
|
||||
parentItem,
|
||||
foldOutPropertyName,
|
||||
togglePropertyName,
|
||||
() => Content(label),
|
||||
flagBits,
|
||||
flagIndex,
|
||||
keyword,
|
||||
onValueChanged: onValueChanged,
|
||||
isVisible: isVisible,
|
||||
bold: bold)
|
||||
{
|
||||
}
|
||||
|
||||
protected TextureRelatedFoldOutItem AddTextureWithRelatedFoldOut(
|
||||
NBShaderRootItem rootItem,
|
||||
ShaderGUIItem parent,
|
||||
string texturePropertyName,
|
||||
string label,
|
||||
string foldOutPropertyName,
|
||||
int wrapFlag,
|
||||
string colorPropertyName = null,
|
||||
Func<bool> isVisible = null)
|
||||
{
|
||||
new TextureItem(
|
||||
rootItem,
|
||||
parent,
|
||||
texturePropertyName,
|
||||
() => Content(label),
|
||||
colorPropertyName,
|
||||
isVisible: isVisible,
|
||||
tillingContentProvider: TillingContent,
|
||||
offsetContentProvider: OffsetContent);
|
||||
TextureRelatedFoldOutItem relatedFoldOut = new TextureRelatedFoldOutItem(
|
||||
rootItem,
|
||||
parent,
|
||||
foldOutPropertyName,
|
||||
texturePropertyName,
|
||||
() => Content(label + "相关功能"),
|
||||
isVisible);
|
||||
new WrapModeItem(rootItem, relatedFoldOut, wrapFlag, () => Content(label + " Wrap"), 2);
|
||||
return relatedFoldOut;
|
||||
}
|
||||
|
||||
protected void AddTextureWithWrap(
|
||||
NBShaderRootItem rootItem,
|
||||
ShaderGUIItem parent,
|
||||
string texturePropertyName,
|
||||
string label,
|
||||
int wrapFlag,
|
||||
string colorPropertyName = null,
|
||||
Func<bool> isVisible = null)
|
||||
{
|
||||
new TextureItem(
|
||||
rootItem,
|
||||
parent,
|
||||
texturePropertyName,
|
||||
() => Content(label),
|
||||
colorPropertyName,
|
||||
isVisible: isVisible,
|
||||
tillingContentProvider: TillingContent,
|
||||
offsetContentProvider: OffsetContent);
|
||||
new WrapModeItem(rootItem, parent, wrapFlag, () => Content(label + " Wrap"), 2, isVisible);
|
||||
}
|
||||
|
||||
protected PropertyToggleBlockItem ToggleBlock(
|
||||
NBShaderRootItem rootItem,
|
||||
string foldOutPropertyName,
|
||||
string togglePropertyName,
|
||||
string label,
|
||||
int flagBits = 0,
|
||||
int flagIndex = 0,
|
||||
ShaderGUIItem parent = null,
|
||||
string keyword = null,
|
||||
Action<bool> onValueChanged = null,
|
||||
Func<bool> isVisible = null,
|
||||
bool bold = false)
|
||||
{
|
||||
return new PropertyToggleBlockItem(
|
||||
rootItem,
|
||||
parent ?? this,
|
||||
foldOutPropertyName,
|
||||
togglePropertyName,
|
||||
() => Content(label),
|
||||
flagBits,
|
||||
flagIndex,
|
||||
keyword,
|
||||
onValueChanged: onValueChanged,
|
||||
isVisible: isVisible,
|
||||
bold: bold);
|
||||
}
|
||||
|
||||
protected static void AddGradient(
|
||||
NBShaderRootItem rootItem,
|
||||
ShaderGUIItem parent,
|
||||
string label,
|
||||
string countPropertyName,
|
||||
string colorPrefix,
|
||||
string alphaPrefix,
|
||||
bool hdr = false,
|
||||
Func<bool> isVisible = null)
|
||||
{
|
||||
new GradientItem(
|
||||
rootItem,
|
||||
parent,
|
||||
countPropertyName,
|
||||
6,
|
||||
BuildPropertyNames(colorPrefix, 6),
|
||||
BuildPropertyNames(alphaPrefix, 3),
|
||||
() => Content(label),
|
||||
hdr,
|
||||
ColorSpace.Gamma,
|
||||
isVisible);
|
||||
}
|
||||
|
||||
protected static string[] BuildPropertyNames(string prefix, int count)
|
||||
{
|
||||
string[] names = new string[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
names[i] = prefix + i;
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
protected static bool IsPropertyMode(NBShaderRootItem rootItem, string propertyName, int expectedMode)
|
||||
{
|
||||
return rootItem.PropertyInfoDic.TryGetValue(propertyName, out ShaderPropertyInfo info) &&
|
||||
!info.Property.hasMixedValue &&
|
||||
Mathf.RoundToInt(info.Property.floatValue) == expectedMode;
|
||||
}
|
||||
|
||||
protected static bool IsPropertyMode(NBShaderRootItem rootItem, string propertyName, params int[] expectedModes)
|
||||
{
|
||||
if (!rootItem.PropertyInfoDic.TryGetValue(propertyName, out ShaderPropertyInfo info) ||
|
||||
info.Property.hasMixedValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int value = Mathf.RoundToInt(info.Property.floatValue);
|
||||
for (int i = 0; i < expectedModes.Length; i++)
|
||||
{
|
||||
if (value == expectedModes[i])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static GUIContent Content(string label)
|
||||
{
|
||||
EnsureCacheLanguage();
|
||||
if (ContentCache.TryGetValue(label, out GUIContent cachedContent))
|
||||
{
|
||||
return cachedContent;
|
||||
}
|
||||
|
||||
cachedContent = NBShaderInspectorLocalization.MakeInspectorContent("feature." + label, label);
|
||||
ContentCache[label] = cachedContent;
|
||||
return cachedContent;
|
||||
}
|
||||
|
||||
protected static GUIContent TillingContent()
|
||||
{
|
||||
return LocalizedInspectorContent(LocalizationTableName, "common.tilling", "Tilling");
|
||||
}
|
||||
|
||||
protected static GUIContent OffsetContent()
|
||||
{
|
||||
return LocalizedInspectorContent(LocalizationTableName, "common.offset", "Offset");
|
||||
}
|
||||
|
||||
internal static string Text(string key, string fallback)
|
||||
{
|
||||
return LocalizedText(LocalizationTableName, key, fallback);
|
||||
}
|
||||
|
||||
internal static string[] PopupOptions(string propertyName, string[] fallback)
|
||||
{
|
||||
EnsureCacheLanguage();
|
||||
if (PopupOptionsCache.TryGetValue(propertyName, out string[] cachedOptions))
|
||||
{
|
||||
return cachedOptions;
|
||||
}
|
||||
|
||||
cachedOptions = NBShaderInspectorLocalization.GetInspectorOptions("feature.popup." + propertyName, fallback);
|
||||
PopupOptionsCache[propertyName] = cachedOptions;
|
||||
return cachedOptions;
|
||||
}
|
||||
|
||||
private static void EnsureCacheLanguage()
|
||||
{
|
||||
string currentLanguage = NBShaderInspectorLocalization.CurrentLanguage;
|
||||
if (string.Equals(s_CacheLanguage, currentLanguage, StringComparison.Ordinal))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
s_CacheLanguage = currentLanguage;
|
||||
ContentCache.Clear();
|
||||
PopupOptionsCache.Clear();
|
||||
}
|
||||
|
||||
internal static bool IsTierKeywordAllowed(NBShaderRootItem rootItem, string keyword)
|
||||
{
|
||||
return string.IsNullOrEmpty(keyword) ||
|
||||
rootItem == null ||
|
||||
rootItem.Context == null ||
|
||||
rootItem.Context.IsKeywordAllowed(keyword);
|
||||
}
|
||||
|
||||
internal static Func<bool> TierVisible(NBShaderRootItem rootItem, string keyword, Func<bool> isVisible = null)
|
||||
{
|
||||
return () => IsTierKeywordAllowed(rootItem, keyword) && (isVisible == null || isVisible());
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class FeaturePopupItem : ShaderGUIPopUpItem
|
||||
{
|
||||
public FeaturePopupItem(
|
||||
ShaderGUIRootItem rootItem,
|
||||
ShaderGUIItem parentItem,
|
||||
string propertyName,
|
||||
Func<GUIContent> contentProvider,
|
||||
string[] popupNames,
|
||||
Action<MaterialProperty> onValueChanged = null,
|
||||
Func<bool> isVisible = null,
|
||||
string keyword = null)
|
||||
: base(
|
||||
rootItem,
|
||||
parentItem,
|
||||
propertyName,
|
||||
contentProvider,
|
||||
() => FeatureToggleFoldOutItem.PopupOptions(propertyName, popupNames),
|
||||
onValueChanged,
|
||||
string.IsNullOrEmpty(keyword)
|
||||
? isVisible
|
||||
: FeatureToggleFoldOutItem.TierVisible(rootItem as NBShaderRootItem, keyword, isVisible))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 355bed04b23c4ea093ad45991f0004a4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using NBShader;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBShaderEditor
|
||||
{
|
||||
internal sealed class FresnelFeatureItem : FeatureToggleFoldOutItem
|
||||
{
|
||||
private static readonly string[] FresnelModeNames = { "颜色", "透明" };
|
||||
|
||||
public FresnelFeatureItem(NBShaderRootItem rootItem, ShaderGUIItem parentItem)
|
||||
: base(rootItem, parentItem, "_FresnelBlockFoldOut", "_fresnelEnabled", "菲涅尔", keyword: "_FRESNEL")
|
||||
{
|
||||
new NBShaderKeywordToggleItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_NB_Debug_Fresnel",
|
||||
"NB_DEBUG_FRESNEL",
|
||||
() => Content("菲涅尔测试颜色"),
|
||||
isVisible: null);
|
||||
new FeaturePopupItem(rootItem, this, "_FresnelMode", () => Content("菲涅尔模式"), FresnelModeNames,
|
||||
property => rootItem.SyncService.ApplyToggleFlag(NBShaderFlags.FLAG_BIT_PARTICLE_FRESNEL_FADE_ON, property.floatValue > 0.5f));
|
||||
Func<bool> isFresnelColorMode = () => IsPropertyMode(rootItem, "_FresnelMode", 0);
|
||||
new ColorItem(rootItem, this, "_FresnelColor", () => Content("菲涅尔颜色"), isFresnelColorMode);
|
||||
new VectorComponentItem(rootItem, this, "_FresnelUnit", 2, () => Content("菲涅尔强度"), true);
|
||||
new VectorComponentItem(rootItem, this, "_FresnelUnit", 0, () => Content("菲涅尔位置"), true, -1f, 1f);
|
||||
new CustomDataSelectItem(rootItem, this, NBShaderFlags.FLAGBIT_POS_0_CUSTOMDATA_FRESNEL_OFFSET, 0, () => Content("菲涅尔位置自定义曲线"));
|
||||
new VectorComponentItem(rootItem, this, "_FresnelUnit", 1, () => Content("菲涅尔范围Pow"), true, 0f, 10f);
|
||||
new VectorComponentItem(rootItem, this, "_FresnelUnit", 3, () => Content("菲涅尔硬度"), true);
|
||||
new ToggleItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_InvertFresnel_Toggle",
|
||||
() => Content("翻转菲涅尔"),
|
||||
enabled => rootItem.SyncService.ApplyToggleFlag(NBShaderFlags.FLAG_BIT_PARTICLE_FRESNEL_INVERT_ON, enabled));
|
||||
new ToggleItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_FresnelColorAffectByAlpha",
|
||||
() => Content("菲涅尔颜色受Alpha影响"),
|
||||
enabled => rootItem.SyncService.ApplyToggleFlag(NBShaderFlags.FLAG_BIT_PARTICLE_FRESNEL_COLOR_AFFETCT_BY_ALPHA, enabled),
|
||||
isFresnelColorMode);
|
||||
new VectorComponentItem(rootItem, this, "_FresnelRotation", 0, () => Content("菲涅尔方向偏移X"), false);
|
||||
new VectorComponentItem(rootItem, this, "_FresnelRotation", 1, () => Content("菲涅尔方向偏移Y"), false);
|
||||
new VectorComponentItem(rootItem, this, "_FresnelRotation", 2, () => Content("菲涅尔方向偏移Z"), false);
|
||||
InitTriggerByChild();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee140beb49b74735960d38a89bd528fd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,171 @@
|
||||
using System;
|
||||
using NBShader;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBShaderEditor
|
||||
{
|
||||
internal sealed class MaskFeatureItem : FeatureToggleFoldOutItem
|
||||
{
|
||||
private static readonly string[] TextureGradientNames = { "贴图", "渐变" };
|
||||
|
||||
public MaskFeatureItem(NBShaderRootItem rootItem, ShaderGUIItem parentItem)
|
||||
: base(rootItem, parentItem, "_MaskBlockFoldOut", "_Mask_Toggle", "遮罩", keyword: "_MASKMAP_ON")
|
||||
{
|
||||
new NBShaderKeywordToggleItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_NB_Debug_Mask",
|
||||
"NB_DEBUG_MASK",
|
||||
() => Content("测试遮罩颜色"),
|
||||
isVisible: null);
|
||||
new VectorComponentItem(rootItem, this, "_MaskMapVec", 0, () => Content("遮罩强度"), true);
|
||||
|
||||
PropertyToggleBlockItem refineBlock = ToggleBlock(
|
||||
rootItem,
|
||||
"_MaskRefineFoldOut",
|
||||
"_MaskRefineToggle",
|
||||
"遮罩整体调整",
|
||||
NBShaderFlags.FLAG_BIT_PARTICLE_1_MASK_REFINE,
|
||||
1,
|
||||
parent: this);
|
||||
new VectorComponentItem(rootItem, refineBlock, "_MaskRefineVec", 0, () => Content("范围(Pow)"), false);
|
||||
new VectorComponentItem(rootItem, refineBlock, "_MaskRefineVec", 1, () => Content("相乘"), false);
|
||||
new VectorComponentItem(rootItem, refineBlock, "_MaskRefineVec", 2, () => Content("偏移(相加)"), false);
|
||||
|
||||
new PNoiseBlendModeItem(rootItem, this, NBShaderFlags.FLAG_BIT_PNOISE_BLEND_POS_0_MASK, "_MaskPNoiseBlendOpacity", () => Content("遮罩程序噪波混合"),
|
||||
() => rootItem.Context.ProgramNoiseEnabled == MixedBool.True);
|
||||
AddMaskMap(rootItem, this, "_MaskMap", "_MaskMapGradientToggle", "_MaskUVModeFoldOut", "遮罩",
|
||||
NBShaderFlags.FLAG_BIT_WRAPMODE_MASKMAP,
|
||||
NBShaderFlags.FLAG_BIT_COLOR_CHANNEL_POS_0_MASKMAP1,
|
||||
NBShaderFlags.FLAG_BIT_UVMODE_POS_0_MASKMAP,
|
||||
NBShaderFlags.FLAG_BIT_PARTICLE_1_MASKMAP_GRADIENT,
|
||||
1,
|
||||
"_MaskMapFoldOut");
|
||||
new CustomDataSelectItem(rootItem, this, NBShaderFlags.FLAGBIT_POS_0_CUSTOMDATA_MASK_OFFSET_X, 0, () => Content("Mask图X轴偏移自定义曲线"));
|
||||
new CustomDataSelectItem(rootItem, this, NBShaderFlags.FLAGBIT_POS_0_CUSTOMDATA_MASK_OFFSET_Y, 0, () => Content("Mask图Y轴偏移自定义曲线"));
|
||||
new Vector2LineItem(rootItem, this, "_MaskMapOffsetAnition", true, () => Content("遮罩偏移速度"));
|
||||
ShaderGUISliderItem maskMapUVRotationItem = new ShaderGUISliderItem(rootItem, this)
|
||||
{
|
||||
PropertyName = "_MaskMapUVRotation",
|
||||
GuiContent = Content("遮罩旋转"),
|
||||
Min = 0f,
|
||||
Max = 360f
|
||||
};
|
||||
maskMapUVRotationItem.InitTriggerByChild();
|
||||
PropertyToggleBlockItem rotateBlock = ToggleBlock(
|
||||
rootItem,
|
||||
"_MaskRotationFoldOut",
|
||||
"_Mask_RotationToggle",
|
||||
"遮罩旋转速度",
|
||||
NBShaderFlags.FLAG_BIT_PARTILCE_MASKMAPROTATIONANIMATION_ON,
|
||||
parent: this);
|
||||
ShaderGUIFloatItem maskMapRotationSpeedItem = new ShaderGUIFloatItem(rootItem, rotateBlock)
|
||||
{
|
||||
PropertyName = "_MaskMapRotationSpeed",
|
||||
GuiContent = Content("旋转速度")
|
||||
};
|
||||
maskMapRotationSpeedItem.InitTriggerByChild();
|
||||
ShaderGUIItem maskNoiseAffect = new NoiseAffectItem(rootItem, this);
|
||||
ShaderGUISliderItem maskDistortionIntensityItem = new ShaderGUISliderItem(rootItem, maskNoiseAffect)
|
||||
{
|
||||
PropertyName = "_MaskDistortion_intensity",
|
||||
GuiContent = Content("遮罩扭曲强度"),
|
||||
RangePropertyName = "MaskDistortionIntensityRangeVec"
|
||||
};
|
||||
maskDistortionIntensityItem.InitTriggerByChild();
|
||||
|
||||
PropertyToggleBlockItem mask2Block = ToggleBlock(
|
||||
rootItem,
|
||||
"_Mask2BlockFoldOut",
|
||||
"_Mask2_Toggle",
|
||||
"遮罩2",
|
||||
parent: this,
|
||||
keyword: "_MASKMAP2_ON");
|
||||
AddMaskMap(rootItem, mask2Block, "_MaskMap2", "_MaskMap2GradientToggle", "_Mask2UVModeFoldOut", "遮罩2",
|
||||
NBShaderFlags.FLAG_BIT_WRAPMODE_MASKMAP2,
|
||||
NBShaderFlags.FLAG_BIT_COLOR_CHANNEL_POS_0_MASKMAP2,
|
||||
NBShaderFlags.FLAG_BIT_UVMODE_POS_0_MASKMAP_2,
|
||||
NBShaderFlags.FLAG_BIT_PARTICLE_1_MASKMAP_2_GRADIENT,
|
||||
1);
|
||||
new VectorComponentItem(rootItem, mask2Block, "_MaskMapVec", 1, () => Content("遮罩2旋转"), false);
|
||||
new Vector2LineItem(rootItem, mask2Block, "_MaskMapOffsetAnition", false, () => Content("遮罩2偏移速度"));
|
||||
|
||||
PropertyToggleBlockItem mask3Block = ToggleBlock(
|
||||
rootItem,
|
||||
"_Mask3BlockFoldOut",
|
||||
"_Mask3_Toggle",
|
||||
"遮罩3",
|
||||
parent: this,
|
||||
keyword: "_MASKMAP3_ON");
|
||||
AddMaskMap(rootItem, mask3Block, "_MaskMap3", "_MaskMap3GradientToggle", "_Mask3UVModeFoldOut", "遮罩3",
|
||||
NBShaderFlags.FLAG_BIT_WRAPMODE_MASKMAP3,
|
||||
NBShaderFlags.FLAG_BIT_COLOR_CHANNEL_POS_0_MASKMAP3,
|
||||
NBShaderFlags.FLAG_BIT_UVMODE_POS_0_MASKMAP_3,
|
||||
NBShaderFlags.FLAG_BIT_PARTICLE_1_MASKMAP_3_GRADIENT,
|
||||
1);
|
||||
new VectorComponentItem(rootItem, mask3Block, "_MaskMapVec", 2, () => Content("遮罩3旋转"), false);
|
||||
new Vector2LineItem(rootItem, mask3Block, "_MaskMap3OffsetAnition", true, () => Content("遮罩3偏移速度"));
|
||||
InitTriggerByChild();
|
||||
}
|
||||
|
||||
private void AddMaskMap(
|
||||
NBShaderRootItem rootItem,
|
||||
ShaderGUIItem parent,
|
||||
string texturePropertyName,
|
||||
string modePropertyName,
|
||||
string uvFoldOutPropertyName,
|
||||
string label,
|
||||
int wrapFlag,
|
||||
int colorChannelFlagPos,
|
||||
int uvModeFlagPos,
|
||||
int gradientFlag,
|
||||
int gradientFlagIndex,
|
||||
string textureFoldOutPropertyName = null)
|
||||
{
|
||||
new FeaturePopupItem(rootItem, parent, modePropertyName, () => Content(label + "模式"), TextureGradientNames,
|
||||
property => rootItem.SyncService.ApplyToggleFlag(gradientFlag, property.floatValue > 0.5f, gradientFlagIndex));
|
||||
ShaderGUIItem textureParent = parent;
|
||||
if (string.IsNullOrEmpty(textureFoldOutPropertyName))
|
||||
{
|
||||
AddTextureWithWrap(rootItem, parent, texturePropertyName, label + "贴图", wrapFlag,
|
||||
isVisible: () => IsPropertyMode(rootItem, modePropertyName, 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
textureParent = AddTextureWithRelatedFoldOut(rootItem, parent, texturePropertyName, label + "贴图", textureFoldOutPropertyName, wrapFlag,
|
||||
isVisible: () => IsPropertyMode(rootItem, modePropertyName, 0));
|
||||
}
|
||||
|
||||
new ColorChannelSelectItem(rootItem, textureParent, colorChannelFlagPos, 0, () => Content(label + "通道选择"),
|
||||
() => IsPropertyMode(rootItem, modePropertyName, 0));
|
||||
AddAlphaGradient(rootItem, parent, label + "渐变", texturePropertyName + "GradientCount", texturePropertyName + "GradientFloat",
|
||||
() => IsPropertyMode(rootItem, modePropertyName, 1));
|
||||
new TextureScaleOffsetItem(rootItem, parent, texturePropertyName, false, () => IsPropertyMode(rootItem, modePropertyName, 1), TillingContent, OffsetContent);
|
||||
new WrapModeItem(rootItem, parent, wrapFlag, () => Content(label + "UV Wrap"), 2,
|
||||
() => IsPropertyMode(rootItem, modePropertyName, 1));
|
||||
new UVModeSelectItem(rootItem, parent, uvFoldOutPropertyName, uvModeFlagPos, 0, () => Content(label + "UV来源"), texturePropertyName, forceEnable: true);
|
||||
}
|
||||
|
||||
private static void AddAlphaGradient(
|
||||
NBShaderRootItem rootItem,
|
||||
ShaderGUIItem parent,
|
||||
string label,
|
||||
string countPropertyName,
|
||||
string alphaPrefix,
|
||||
Func<bool> isVisible = null)
|
||||
{
|
||||
new GradientItem(
|
||||
rootItem,
|
||||
parent,
|
||||
countPropertyName,
|
||||
6,
|
||||
Array.Empty<string>(),
|
||||
BuildPropertyNames(alphaPrefix, 3),
|
||||
() => Content(label),
|
||||
false,
|
||||
ColorSpace.Gamma,
|
||||
isVisible);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c5fe236301944650a35adc509015737c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,519 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using NBShader;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace NBShaderEditor
|
||||
{
|
||||
internal sealed class NoiseAndDistortFeatureItem : FeatureToggleFoldOutItem
|
||||
{
|
||||
private static readonly string[] DistortModeNames = { "FlowMap/RG贴图", "折射率" };
|
||||
private static readonly string[] ScreenDistortModeNames = { "No Screen Distort", "Deferred Distort", "Camera Opaque Distort" };
|
||||
private const int ScreenDistortModeDeferred = 1;
|
||||
private const int ScreenDistortModeCameraOpaque = 2;
|
||||
private const string ScreenDistortKeyword = "_SCREEN_DISTORT_MODE";
|
||||
private const string ScreenDistortModePropertyName = "_ScreenDistortModeToggle";
|
||||
private const string RendererDataListPropertyName = "m_RendererDataList";
|
||||
private const string LegacyRendererDataPropertyName = "m_RendererData";
|
||||
private const string RendererFeaturesPropertyName = "m_RendererFeatures";
|
||||
private const string OpaqueTexturePropertyName = "supportsCameraOpaqueTexture";
|
||||
private const string OpaqueTextureSerializedPropertyName = "m_RequireOpaqueTexture";
|
||||
private const string RendererFeatureActivePropertyName = "isActive";
|
||||
private const string RendererFeatureActiveSerializedPropertyName = "m_Active";
|
||||
private const string NBPostProcessTypeFullName = "NBShader.NBPostProcess";
|
||||
private const string NBPostProcessTypeName = "NBPostProcess";
|
||||
|
||||
public NoiseAndDistortFeatureItem(NBShaderRootItem rootItem, ShaderGUIItem parentItem)
|
||||
: base(rootItem, parentItem, "_NoiseBlockFoldOut", "_noisemapEnabled", "扭曲", keyword: "_NOISEMAP")
|
||||
{
|
||||
new NBShaderKeywordToggleItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_NB_Debug_Distort",
|
||||
"NB_DEBUG_DISTORT",
|
||||
() => Content("扭曲强度值测试"),
|
||||
isVisible: null);
|
||||
ShaderGUISliderItem noiseIntensityItem = new ShaderGUISliderItem(rootItem, this)
|
||||
{
|
||||
PropertyName = "_NoiseIntensity",
|
||||
GuiContent = Content("整体扭曲强度"),
|
||||
RangePropertyName = "_NoiseIntensityRangeVec"
|
||||
};
|
||||
noiseIntensityItem.InitTriggerByChild();
|
||||
new CustomDataSelectItem(rootItem, this, NBShaderFlags.FLAGBIT_POS_1_CUSTOMDATA_NOISE_INTENSITY, 1, () => Content("扭曲强度自定义曲线"));
|
||||
new FeaturePopupItem(rootItem, this, "_ScreenDistortModeToggle", () => Content("屏幕扰动模式"), ScreenDistortModeNames,
|
||||
property => rootItem.SyncService.ApplyScreenDistortMode(Mathf.RoundToInt(property.floatValue)),
|
||||
() => rootItem.Context.UIEffectEnabled != MixedBool.True,
|
||||
ScreenDistortKeyword);
|
||||
Func<bool> isDeferredDistortVisible = TierVisible(
|
||||
rootItem,
|
||||
ScreenDistortKeyword,
|
||||
() => IsScreenDistortMode(rootItem, ScreenDistortModeDeferred));
|
||||
Func<bool> isCameraOpaqueDistortVisible = TierVisible(
|
||||
rootItem,
|
||||
ScreenDistortKeyword,
|
||||
() => IsScreenDistortMode(rootItem, ScreenDistortModeCameraOpaque));
|
||||
Func<bool> isScreenDistortModeNeedsNBPostProcessVisible = () => isDeferredDistortVisible() || isCameraOpaqueDistortVisible();
|
||||
Func<bool> isMissingNBPostProcessVisible = () => isScreenDistortModeNeedsNBPostProcessVisible() && !HasActiveNBPostProcessRendererFeature();
|
||||
Func<bool> isMissingOpaqueTextureVisible = () => isCameraOpaqueDistortVisible() && !HasCameraOpaqueTextureCopyEnabled();
|
||||
new PingableHelpBoxItem(
|
||||
rootItem,
|
||||
this,
|
||||
() => Text(
|
||||
"feature.screenDistort.missingNbPostProcess.message",
|
||||
"Screen Distort requires an active NB Post Process Feature on at least one RendererData in the current URP Pipeline Asset."),
|
||||
MessageType.Warning,
|
||||
() => ButtonContent("feature.screenDistort.pingRendererData", "Ping Renderer"),
|
||||
GetRendererDataForNBPostProcessPing,
|
||||
isMissingNBPostProcessVisible);
|
||||
new PingableHelpBoxItem(
|
||||
rootItem,
|
||||
this,
|
||||
() => Text(
|
||||
"feature.screenDistort.missingOpaqueTextureCopy.message",
|
||||
"Camera Opaque Distort requires Opaque Texture to be enabled on the current URP Pipeline Asset."),
|
||||
MessageType.Warning,
|
||||
() => ButtonContent("feature.screenDistort.pingPipelineAsset", "Ping URP Asset"),
|
||||
GetCurrentRenderPipelineAsset,
|
||||
isMissingOpaqueTextureVisible);
|
||||
ShaderGUISliderItem screenDistortIntensityItem = new ShaderGUISliderItem(
|
||||
rootItem,
|
||||
this,
|
||||
TierVisible(
|
||||
rootItem,
|
||||
ScreenDistortKeyword,
|
||||
() => rootItem.Context.UIEffectEnabled != MixedBool.True && IsPropertyGreater(rootItem, "_ScreenDistortModeToggle", 0.5f)))
|
||||
{
|
||||
PropertyName = "_ScreenDistortIntensity",
|
||||
GuiContent = Content("屏幕扭曲强度"),
|
||||
RangePropertyName = "_ScreenDistortIntensityRangeVec"
|
||||
};
|
||||
screenDistortIntensityItem.InitTriggerByChild();
|
||||
new ToggleItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_DisableMainPassToggle",
|
||||
() => Content("关闭主材质Pass"),
|
||||
enabled =>
|
||||
{
|
||||
rootItem.SyncService.ApplyScreenDistortMode(GetIntProperty(rootItem, "_ScreenDistortModeToggle"));
|
||||
},
|
||||
TierVisible(
|
||||
rootItem,
|
||||
ScreenDistortKeyword,
|
||||
() => rootItem.Context.UIEffectEnabled != MixedBool.True && IsPropertyGreater(rootItem, "_ScreenDistortModeToggle", 0.5f)));
|
||||
|
||||
PropertyToggleBlockItem screenAlphaBlock = ToggleBlock(
|
||||
rootItem,
|
||||
"_ScreenDistortAlphaFoldOut",
|
||||
"_ScreenDistortAlphaRefineToggle",
|
||||
"屏幕扭曲Alpha整体调整",
|
||||
NBShaderFlags.FLAG_BIT_PARTICLE_1_SCREEN_DISTORT_ALPHA_REFINE,
|
||||
1,
|
||||
parent: this,
|
||||
isVisible: TierVisible(
|
||||
rootItem,
|
||||
ScreenDistortKeyword,
|
||||
() => rootItem.Context.UIEffectEnabled != MixedBool.True && IsPropertyGreater(rootItem, "_ScreenDistortModeToggle", 0.5f)));
|
||||
ShaderGUIFloatItem screenDistortAlphaPowItem = new ShaderGUIFloatItem(rootItem, screenAlphaBlock)
|
||||
{
|
||||
PropertyName = "_ScreenDistortAlphaPow",
|
||||
GuiContent = Content("范围(Pow)")
|
||||
};
|
||||
screenDistortAlphaPowItem.InitTriggerByChild();
|
||||
ShaderGUIFloatItem screenDistortAlphaMultiItem = new ShaderGUIFloatItem(rootItem, screenAlphaBlock)
|
||||
{
|
||||
PropertyName = "_ScreenDistortAlphaMulti",
|
||||
GuiContent = Content("相乘")
|
||||
};
|
||||
screenDistortAlphaMultiItem.InitTriggerByChild();
|
||||
ShaderGUIFloatItem screenDistortAlphaAddItem = new ShaderGUIFloatItem(rootItem, screenAlphaBlock)
|
||||
{
|
||||
PropertyName = "_ScreenDistortAlphaAdd",
|
||||
GuiContent = Content("偏移(相加)")
|
||||
};
|
||||
screenDistortAlphaAddItem.InitTriggerByChild();
|
||||
|
||||
new FeaturePopupItem(rootItem, this, "_DistortMode", () => Content("扭曲模式"), DistortModeNames,
|
||||
property => rootItem.SyncService.ApplyToggleKeyword("_DISTORT_REFRACTION", property.floatValue > 0.5f),
|
||||
keyword: "_DISTORT_REFRACTION");
|
||||
TextureRelatedFoldOutItem noiseMapRelatedFoldOut = AddTextureWithRelatedFoldOut(rootItem, this, "_NoiseMap", "扭曲贴图", "_NoiseMapFoldOut", NBShaderFlags.FLAG_BIT_WRAPMODE_NOISEMAP,
|
||||
isVisible: () => IsPropertyMode(rootItem, "_DistortMode", 0));
|
||||
new UVModeSelectItem(rootItem, noiseMapRelatedFoldOut, "_NoiseUVModeFoldOut", NBShaderFlags.FLAG_BIT_UVMODE_POS_0_NOISE_MAP, 0, () => Content("扭曲贴图UV来源"), "_NoiseMap",
|
||||
isVisible: () => IsPropertyMode(rootItem, "_DistortMode", 0));
|
||||
new Vector2LineItem(rootItem, noiseMapRelatedFoldOut, "_DistortionDirection", true, () => Content("扭曲方向强度"), () => IsPropertyMode(rootItem, "_DistortMode", 0));
|
||||
new CustomDataSelectItem(rootItem, noiseMapRelatedFoldOut, NBShaderFlags.FLAGBIT_POS_2_CUSTOMDATA_NOISE_DIRECTION_X, 2, () => Content("扭曲方向强度X自定义曲线"), () => IsPropertyMode(rootItem, "_DistortMode", 0));
|
||||
new CustomDataSelectItem(rootItem, noiseMapRelatedFoldOut, NBShaderFlags.FLAGBIT_POS_2_CUSTOMDATA_NOISE_DIRECTION_Y, 2, () => Content("扭曲方向强度Y自定义曲线"), () => IsPropertyMode(rootItem, "_DistortMode", 0));
|
||||
ShaderGUISliderItem noiseMapUVRotationItem = new ShaderGUISliderItem(rootItem, noiseMapRelatedFoldOut, () => IsPropertyMode(rootItem, "_DistortMode", 0))
|
||||
{
|
||||
PropertyName = "_NoiseMapUVRotation",
|
||||
GuiContent = Content("扭曲旋转"),
|
||||
Min = 0f,
|
||||
Max = 360f
|
||||
};
|
||||
noiseMapUVRotationItem.InitTriggerByChild();
|
||||
new Vector2LineItem(rootItem, noiseMapRelatedFoldOut, "_NoiseOffset", true, () => Content("扭曲偏移速度"), () => IsPropertyMode(rootItem, "_DistortMode", 0));
|
||||
new ToggleItem(
|
||||
rootItem,
|
||||
noiseMapRelatedFoldOut,
|
||||
"_DistortionBothDirection_Toggle",
|
||||
() => Content("0.5为中值,双向扭曲"),
|
||||
enabled => rootItem.SyncService.ApplyToggleFlag(NBShaderFlags.FLAG_BIT_PARTICLE_NOISEMAP_NORMALIZEED_ON, enabled),
|
||||
() => IsPropertyMode(rootItem, "_DistortMode", 0));
|
||||
ShaderGUISliderItem refractionIorItem = new ShaderGUISliderItem(
|
||||
rootItem,
|
||||
this,
|
||||
TierVisible(rootItem, "_DISTORT_REFRACTION", () => IsPropertyMode(rootItem, "_DistortMode", 1)))
|
||||
{
|
||||
PropertyName = "_RefractionIOR",
|
||||
GuiContent = Content("折射率"),
|
||||
Min = 0f,
|
||||
Max = 5f
|
||||
};
|
||||
refractionIorItem.InitTriggerByChild();
|
||||
new PNoiseBlendModeItem(rootItem, this, NBShaderFlags.FLAG_BIT_PNOISE_BLEND_POS_0_DISTORT, "_DistortPNoiseBlendOpacity", () => Content("扭曲程序噪波混合"),
|
||||
() => rootItem.Context.ProgramNoiseEnabled == MixedBool.True);
|
||||
|
||||
PropertyToggleBlockItem noiseMaskBlock = ToggleBlock(
|
||||
rootItem,
|
||||
"_NoiseMaskBlockFoldOut",
|
||||
"_noiseMaskMap_Toggle",
|
||||
"扭曲遮罩",
|
||||
parent: this,
|
||||
keyword: "_NOISE_MASKMAP");
|
||||
AddTextureWithWrap(rootItem, noiseMaskBlock, "_NoiseMaskMap", "扭曲遮罩贴图", NBShaderFlags.FLAG_BIT_WRAPMODE_NOISE_MASKMAP);
|
||||
new ColorChannelSelectItem(rootItem, noiseMaskBlock, NBShaderFlags.FLAG_BIT_COLOR_CHANNEL_POS_0_NOISE_MASK, 0, () => Content("扭曲遮罩图通道选择"));
|
||||
new UVModeSelectItem(rootItem, noiseMaskBlock, "_NoiseMaskUVModeFoldOut", NBShaderFlags.FLAG_BIT_UVMODE_POS_0_NOISE_MASK_MAP, 0, () => Content("扭曲遮罩贴图UV来源"), "_NoiseMaskMap");
|
||||
|
||||
InitTriggerByChild();
|
||||
}
|
||||
|
||||
private static int GetIntProperty(NBShaderRootItem rootItem, string propertyName)
|
||||
{
|
||||
return rootItem.PropertyInfoDic.TryGetValue(propertyName, out ShaderPropertyInfo info) && !info.Property.hasMixedValue
|
||||
? Mathf.RoundToInt(info.Property.floatValue)
|
||||
: 0;
|
||||
}
|
||||
|
||||
private static bool IsPropertyGreater(NBShaderRootItem rootItem, string propertyName, float threshold)
|
||||
{
|
||||
return rootItem.PropertyInfoDic.TryGetValue(propertyName, out ShaderPropertyInfo info) &&
|
||||
!info.Property.hasMixedValue &&
|
||||
info.Property.floatValue > threshold;
|
||||
}
|
||||
|
||||
private static bool IsScreenDistortMode(NBShaderRootItem rootItem, int mode)
|
||||
{
|
||||
return rootItem.Context.UIEffectEnabled != MixedBool.True &&
|
||||
IsPropertyMode(rootItem, ScreenDistortModePropertyName, mode);
|
||||
}
|
||||
|
||||
private static bool HasActiveNBPostProcessRendererFeature()
|
||||
{
|
||||
try
|
||||
{
|
||||
RenderPipelineAsset pipelineAsset = GetCurrentRenderPipelineAsset();
|
||||
if (pipelineAsset == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SerializedObject pipelineObject = new SerializedObject(pipelineAsset);
|
||||
SerializedProperty rendererDataList = pipelineObject.FindProperty(RendererDataListPropertyName);
|
||||
if (rendererDataList != null && rendererDataList.isArray)
|
||||
{
|
||||
for (int i = 0; i < rendererDataList.arraySize; i++)
|
||||
{
|
||||
UnityEngine.Object rendererData = rendererDataList.GetArrayElementAtIndex(i).objectReferenceValue;
|
||||
if (RendererDataHasNBPostProcess(rendererData, requireActive: true))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
SerializedProperty legacyRendererData = pipelineObject.FindProperty(LegacyRendererDataPropertyName);
|
||||
return legacyRendererData != null && RendererDataHasNBPostProcess(legacyRendererData.objectReferenceValue, requireActive: true);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool RendererDataHasNBPostProcess(UnityEngine.Object rendererData, bool requireActive)
|
||||
{
|
||||
if (rendererData == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SerializedObject rendererDataObject = new SerializedObject(rendererData);
|
||||
SerializedProperty rendererFeatures = rendererDataObject.FindProperty(RendererFeaturesPropertyName);
|
||||
if (rendererFeatures == null || !rendererFeatures.isArray)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rendererFeatures.arraySize; i++)
|
||||
{
|
||||
UnityEngine.Object rendererFeature = rendererFeatures.GetArrayElementAtIndex(i).objectReferenceValue;
|
||||
if (IsNBPostProcessFeature(rendererFeature) &&
|
||||
(!requireActive || IsRendererFeatureActive(rendererFeature)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsNBPostProcessFeature(UnityEngine.Object rendererFeature)
|
||||
{
|
||||
if (rendererFeature == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Type type = rendererFeature.GetType(); type != null; type = type.BaseType)
|
||||
{
|
||||
if (string.Equals(type.FullName, NBPostProcessTypeFullName, StringComparison.Ordinal) ||
|
||||
string.Equals(type.Name, NBPostProcessTypeName, StringComparison.Ordinal))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsRendererFeatureActive(UnityEngine.Object rendererFeature)
|
||||
{
|
||||
if (rendererFeature == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TryGetBoolProperty(rendererFeature, RendererFeatureActivePropertyName, out bool isActive))
|
||||
{
|
||||
return isActive;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
SerializedObject rendererFeatureObject = new SerializedObject(rendererFeature);
|
||||
SerializedProperty activeProperty = rendererFeatureObject.FindProperty(RendererFeatureActiveSerializedPropertyName);
|
||||
return activeProperty != null &&
|
||||
activeProperty.propertyType == SerializedPropertyType.Boolean &&
|
||||
activeProperty.boolValue;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool HasCameraOpaqueTextureCopyEnabled()
|
||||
{
|
||||
try
|
||||
{
|
||||
RenderPipelineAsset pipelineAsset = GetCurrentRenderPipelineAsset();
|
||||
if (pipelineAsset == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TryGetBoolProperty(pipelineAsset, OpaqueTexturePropertyName, out bool supportsCameraOpaqueTexture))
|
||||
{
|
||||
return supportsCameraOpaqueTexture;
|
||||
}
|
||||
|
||||
SerializedObject pipelineObject = new SerializedObject(pipelineAsset);
|
||||
SerializedProperty opaqueTextureProperty = pipelineObject.FindProperty(OpaqueTextureSerializedPropertyName);
|
||||
return opaqueTextureProperty != null &&
|
||||
opaqueTextureProperty.propertyType == SerializedPropertyType.Boolean &&
|
||||
opaqueTextureProperty.boolValue;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static UnityEngine.Object GetRendererDataForNBPostProcessPing()
|
||||
{
|
||||
try
|
||||
{
|
||||
RenderPipelineAsset pipelineAsset = GetCurrentRenderPipelineAsset();
|
||||
if (pipelineAsset == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
SerializedObject pipelineObject = new SerializedObject(pipelineAsset);
|
||||
SerializedProperty rendererDataList = pipelineObject.FindProperty(RendererDataListPropertyName);
|
||||
if (rendererDataList != null && rendererDataList.isArray)
|
||||
{
|
||||
return GetRendererDataForNBPostProcessPing(rendererDataList);
|
||||
}
|
||||
|
||||
SerializedProperty legacyRendererData = pipelineObject.FindProperty(LegacyRendererDataPropertyName);
|
||||
return legacyRendererData != null ? legacyRendererData.objectReferenceValue : null;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static UnityEngine.Object GetRendererDataForNBPostProcessPing(SerializedProperty rendererDataList)
|
||||
{
|
||||
UnityEngine.Object firstRendererData = null;
|
||||
UnityEngine.Object rendererDataWithInactiveFeature = null;
|
||||
for (int i = 0; i < rendererDataList.arraySize; i++)
|
||||
{
|
||||
UnityEngine.Object rendererData = rendererDataList.GetArrayElementAtIndex(i).objectReferenceValue;
|
||||
if (rendererData == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (firstRendererData == null)
|
||||
{
|
||||
firstRendererData = rendererData;
|
||||
}
|
||||
|
||||
if (RendererDataHasNBPostProcess(rendererData, requireActive: true))
|
||||
{
|
||||
return rendererData;
|
||||
}
|
||||
|
||||
if (rendererDataWithInactiveFeature == null &&
|
||||
RendererDataHasNBPostProcess(rendererData, requireActive: false))
|
||||
{
|
||||
rendererDataWithInactiveFeature = rendererData;
|
||||
}
|
||||
}
|
||||
|
||||
return rendererDataWithInactiveFeature != null ? rendererDataWithInactiveFeature : firstRendererData;
|
||||
}
|
||||
|
||||
private static RenderPipelineAsset GetCurrentRenderPipelineAsset()
|
||||
{
|
||||
return QualitySettings.renderPipeline != null
|
||||
? QualitySettings.renderPipeline
|
||||
: GraphicsSettings.currentRenderPipeline;
|
||||
}
|
||||
|
||||
private static bool TryGetBoolProperty(UnityEngine.Object target, string propertyName, out bool value)
|
||||
{
|
||||
value = false;
|
||||
if (target == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
PropertyInfo propertyInfo = target.GetType().GetProperty(
|
||||
propertyName,
|
||||
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
if (propertyInfo == null || propertyInfo.PropertyType != typeof(bool))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
value = (bool)propertyInfo.GetValue(target, null);
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static GUIContent ButtonContent(string key, string fallback)
|
||||
{
|
||||
return NBShaderInspectorLocalization.MakeContent("inspector." + key + ".button", fallback);
|
||||
}
|
||||
|
||||
private sealed class PingableHelpBoxItem : ShaderGUIItem
|
||||
{
|
||||
private const float HelpBoxPadding = 6f;
|
||||
private const float HelpBoxButtonGap = 4f;
|
||||
private const float MaxButtonWidth = 160f;
|
||||
private static readonly GUIContent TempHelpContent = new GUIContent();
|
||||
|
||||
private readonly Func<string> _messageProvider;
|
||||
private readonly MessageType _messageType;
|
||||
private readonly Func<GUIContent> _buttonContentProvider;
|
||||
private readonly Func<UnityEngine.Object> _targetProvider;
|
||||
private readonly Func<bool> _isVisible;
|
||||
|
||||
public PingableHelpBoxItem(
|
||||
ShaderGUIRootItem rootItem,
|
||||
ShaderGUIItem parentItem,
|
||||
Func<string> messageProvider,
|
||||
MessageType messageType,
|
||||
Func<GUIContent> buttonContentProvider,
|
||||
Func<UnityEngine.Object> targetProvider,
|
||||
Func<bool> isVisible) : base(rootItem, parentItem)
|
||||
{
|
||||
_messageProvider = messageProvider ?? (() => string.Empty);
|
||||
_messageType = messageType;
|
||||
_buttonContentProvider = buttonContentProvider ?? (() => GUIContent.none);
|
||||
_targetProvider = targetProvider ?? (() => null);
|
||||
_isVisible = isVisible;
|
||||
}
|
||||
|
||||
public override void OnGUI()
|
||||
{
|
||||
if (_isVisible != null && !_isVisible())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using (ParentControlDisabledScope())
|
||||
{
|
||||
string message = _messageProvider();
|
||||
GUIContent buttonContent = _buttonContentProvider();
|
||||
TempHelpContent.text = message;
|
||||
float width = Mathf.Max(1f, EditorGUIUtility.currentViewWidth + GlobalRectWidthExpansion + GlobalRectXOffset);
|
||||
float messageHeight = Mathf.Max(
|
||||
EditorGUIUtility.singleLineHeight * 2f,
|
||||
EditorStyles.helpBox.CalcHeight(TempHelpContent, width));
|
||||
float buttonHeight = EditorGUIUtility.singleLineHeight;
|
||||
float height = messageHeight + HelpBoxButtonGap + buttonHeight + HelpBoxPadding;
|
||||
Rect rect = ApplyGlobalRectCompensation(LayoutRect(height));
|
||||
EditorGUI.HelpBox(rect, message, _messageType);
|
||||
|
||||
float buttonWidth = Mathf.Min(
|
||||
MaxButtonWidth,
|
||||
Mathf.Max(90f, EditorStyles.miniButton.CalcSize(buttonContent).x + 18f));
|
||||
buttonWidth = Mathf.Min(buttonWidth, Mathf.Max(0f, rect.width - HelpBoxPadding * 2f));
|
||||
Rect buttonRect = new Rect(
|
||||
rect.xMax - HelpBoxPadding - buttonWidth,
|
||||
rect.yMax - HelpBoxPadding - buttonHeight,
|
||||
buttonWidth,
|
||||
buttonHeight);
|
||||
|
||||
UnityEngine.Object target = _targetProvider();
|
||||
using (new EditorGUI.DisabledScope(target == null))
|
||||
{
|
||||
if (GUI.Button(buttonRect, buttonContent, EditorStyles.miniButton))
|
||||
{
|
||||
Selection.activeObject = target;
|
||||
EditorGUIUtility.PingObject(target);
|
||||
}
|
||||
}
|
||||
|
||||
TempHelpContent.text = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e4f64374c74c47478edfdd0ddb6e8355
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using NBShader;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBShaderEditor
|
||||
{
|
||||
internal sealed class ParallaxFeatureItem : FeatureToggleFoldOutItem
|
||||
{
|
||||
public ParallaxFeatureItem(NBShaderRootItem rootItem, ShaderGUIItem parentItem)
|
||||
: base(rootItem, parentItem, "_ParallaxBlockFoldOut", "_ParallaxMapping_Toggle", "遮蔽视差", keyword: "_PARALLAX_MAPPING", isVisible: () => rootItem.Context.UIEffectEnabled != MixedBool.True)
|
||||
{
|
||||
AddTextureWithWrap(rootItem, this, "_ParallaxMapping_Map", "视差贴图", NBShaderFlags.FLAG_BIT_WRAPMODE_PARALLAXMAPPINGMAP);
|
||||
ShaderGUISliderItem parallaxMappingIntensityItem = new ShaderGUISliderItem(rootItem, this)
|
||||
{
|
||||
PropertyName = "_ParallaxMapping_Intensity",
|
||||
GuiContent = Content("视差"),
|
||||
RangePropertyName = "_ParallaxMapping_IntensityRangeVec"
|
||||
};
|
||||
parallaxMappingIntensityItem.InitTriggerByChild();
|
||||
new VectorComponentItem(rootItem, this, "_ParallaxMapping_Vec", 0, () => Content("遮蔽视差最小层数"), true, 0f, 100f);
|
||||
new VectorComponentItem(rootItem, this, "_ParallaxMapping_Vec", 1, () => Content("遮蔽视差最大层数"), true, 0f, 100f);
|
||||
new HelpBoxItem(rootItem, this, () => Text("feature.parallax.layerWarning.message", "遮蔽视差层数过高将影响性能"), MessageType.Warning,
|
||||
() => IsParallaxMaxLayerHigh(rootItem));
|
||||
InitTriggerByChild();
|
||||
}
|
||||
|
||||
private static bool IsParallaxMaxLayerHigh(NBShaderRootItem rootItem)
|
||||
{
|
||||
return rootItem.PropertyInfoDic.TryGetValue("_ParallaxMapping_Vec", out ShaderPropertyInfo info) &&
|
||||
!info.Property.hasMixedValue &&
|
||||
info.Property.vectorValue.y >= 20f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d5bf38ca73454cc1916d18f40182d5ad
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using NBShader;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBShaderEditor
|
||||
{
|
||||
internal sealed class PortalFeatureItem : FeatureToggleFoldOutItem
|
||||
{
|
||||
public PortalFeatureItem(NBShaderRootItem rootItem, ShaderGUIItem parentItem)
|
||||
: base(rootItem, parentItem, "_PortalBlockFoldOut", "_Portal_Toggle", "模板视差", onValueChanged: _ => rootItem.SyncService.ApplyPortalState(), isVisible: () => rootItem.Context.UIEffectEnabled != MixedBool.True)
|
||||
{
|
||||
new ToggleItem(rootItem, this, "_Portal_MaskToggle", () => Content("模板视差蒙版"), _ => rootItem.SyncService.ApplyPortalState());
|
||||
InitTriggerByChild();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 67c4fd7f472848dfbefe5703bb834b60
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using NBShader;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBShaderEditor
|
||||
{
|
||||
internal sealed class ProgramNoiseFeatureItem : FeatureToggleFoldOutItem
|
||||
{
|
||||
public ProgramNoiseFeatureItem(NBShaderRootItem rootItem, ShaderGUIItem parentItem)
|
||||
: base(rootItem, parentItem, "_ProgramNoiseBlockFoldOut", "_ProgramNoise_Toggle", "程序化噪波", keyword: "_PROGRAM_NOISE")
|
||||
{
|
||||
new NBShaderKeywordToggleItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_NB_Debug_PNoise",
|
||||
"NB_DEBUG_PNOISE",
|
||||
() => Content("程序化噪波测试颜色"),
|
||||
isVisible: null);
|
||||
new UVModeSelectItem(rootItem, this, "_ProgramNoiseUVModeFoldOut", NBShaderFlags.FLAG_BIT_UVMODE_POS_0_PROGRAM_NOISE, 0, () => Content("程序噪波UV来源"), forceEnable: true);
|
||||
ShaderGUIFloatItem programNoiseRotateItem = new ShaderGUIFloatItem(rootItem, this)
|
||||
{
|
||||
PropertyName = "_ProgramNoise_Rotate",
|
||||
GuiContent = Content("程序化噪波旋转")
|
||||
};
|
||||
programNoiseRotateItem.InitTriggerByChild();
|
||||
|
||||
PropertyToggleBlockItem simpleBlock = ToggleBlock(rootItem, "_ProgramNoiseSimpleFoldOut", "_ProgramNoise_Simple_Toggle", "Perlin噪波",
|
||||
parent: this, keyword: "_PROGRAM_NOISE_SIMPLE");
|
||||
new Vector2LineItem(rootItem, simpleBlock, "_DissolveVoronoi_Vec", true, () => Content("噪波1缩放"));
|
||||
new VectorComponentItem(rootItem, simpleBlock, "_DissolveVoronoi_Vec2", 2, () => Content("噪波1速度"), false);
|
||||
new Vector2LineItem(rootItem, simpleBlock, "_DissolveVoronoi_Vec4", true, () => Content("噪波1偏移"));
|
||||
new Vector2LineItem(rootItem, simpleBlock, "_DissolveVoronoi_Vec3", true, () => Content("噪波1偏移速度"));
|
||||
new CustomDataSelectItem(rootItem, simpleBlock, NBShaderFlags.FLAGBIT_POS_2_CUSTOMDATA_DISSOLVE_NOISE1_OFFSET_X, 2, () => Content("噪波1偏移速度X自定义曲线"));
|
||||
new CustomDataSelectItem(rootItem, simpleBlock, NBShaderFlags.FLAGBIT_POS_2_CUSTOMDATA_DISSOLVE_NOISE1_OFFSET_Y, 2, () => Content("噪波1偏移速度Y自定义曲线"));
|
||||
|
||||
PropertyToggleBlockItem voronoiBlock = ToggleBlock(rootItem, "_ProgramNoiseVoronoiFoldOut", "_ProgramNoise_Voronoi_Toggle", "Voronoi噪波",
|
||||
parent: this, keyword: "_PROGRAM_NOISE_VORONOI");
|
||||
new Vector2LineItem(rootItem, voronoiBlock, "_DissolveVoronoi_Vec", false, () => Content("噪波2缩放"));
|
||||
new VectorComponentItem(rootItem, voronoiBlock, "_DissolveVoronoi_Vec2", 3, () => Content("噪波2速度"), false);
|
||||
new Vector2LineItem(rootItem, voronoiBlock, "_DissolveVoronoi_Vec4", false, () => Content("噪波2偏移"));
|
||||
new Vector2LineItem(rootItem, voronoiBlock, "_DissolveVoronoi_Vec3", false, () => Content("噪波2偏移速度"));
|
||||
new CustomDataSelectItem(rootItem, voronoiBlock, NBShaderFlags.FLAGBIT_POS_2_CUSTOMDATA_DISSOLVE_NOISE2_OFFSET_X, 2, () => Content("噪波2偏移速度X自定义曲线"));
|
||||
new CustomDataSelectItem(rootItem, voronoiBlock, NBShaderFlags.FLAGBIT_POS_2_CUSTOMDATA_DISSOLVE_NOISE2_OFFSET_Y, 2, () => Content("噪波2偏移速度Y自定义曲线"));
|
||||
new VectorComponentItem(rootItem, this, "_DissolveVoronoi_Vec2", 0, () => Content("噪波1和噪波2混合系数"), true);
|
||||
new PNoiseBlendModeItem(
|
||||
rootItem,
|
||||
this,
|
||||
NBShaderFlags.FLAG_BIT_PNOISE_BLEND_POS_0_BASE_BLEND,
|
||||
"_ProgramNoiseBaseBlendOpacity",
|
||||
() => Content("两种程序噪波混合"),
|
||||
() => rootItem.Context.ProgramNoiseEnabled == MixedBool.True &&
|
||||
rootItem.Context.IsToggleOn("_ProgramNoise_Simple_Toggle") &&
|
||||
rootItem.Context.IsToggleOn("_ProgramNoise_Voronoi_Toggle"));
|
||||
InitTriggerByChild();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c6a446a2f0364fb0844cb9d36bd39a27
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using NBShader;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBShaderEditor
|
||||
{
|
||||
internal sealed class RampColorFeatureItem : FeatureToggleFoldOutItem
|
||||
{
|
||||
private static readonly string[] RampSourceNames = { "渐变", "贴图" };
|
||||
private static readonly string[] BlendModeNames = { "叠加", "相乘" };
|
||||
|
||||
public RampColorFeatureItem(NBShaderRootItem rootItem, ShaderGUIItem parentItem)
|
||||
: base(rootItem, parentItem, "_RampColorBlockFoldOut", "_RampColorToggle", "颜色映射(Ramp)", keyword: "_COLOR_RAMP")
|
||||
{
|
||||
Func<bool> isRampMapVisible = TierVisible(rootItem, "_COLOR_RAMP_MAP", () => IsPropertyMode(rootItem, "_RampColorSourceMode", 1));
|
||||
new FeaturePopupItem(rootItem, this, "_RampColorSourceMode", () => Content("Ramp来源模式"), RampSourceNames,
|
||||
_ => rootItem.SyncService.SyncMaterialState(),
|
||||
keyword: "_COLOR_RAMP_MAP");
|
||||
AddTextureWithWrap(rootItem, this, "_RampColorMap", "颜色映射黑白图", NBShaderFlags.FLAG_BIT_WRAPMODE_RAMP_COLOR_MAP,
|
||||
isVisible: isRampMapVisible);
|
||||
new TextureScaleOffsetItem(rootItem, this, "_RampColorMap", false, () => IsPropertyMode(rootItem, "_RampColorSourceMode", 0), TillingContent, OffsetContent);
|
||||
new WrapModeItem(rootItem, this, NBShaderFlags.FLAG_BIT_WRAPMODE_RAMP_COLOR_MAP, () => Content("颜色映射UV Wrap"), 2,
|
||||
() => IsPropertyMode(rootItem, "_RampColorSourceMode", 0));
|
||||
new ColorChannelSelectItem(rootItem, this, NBShaderFlags.FLAG_BIT_COLOR_CHANNEL_POS_0_RAMP_COLOR_MAP, 0, () => Content("颜色映射黑白图通道选择"),
|
||||
isRampMapVisible);
|
||||
new UVModeSelectItem(rootItem, this, "_RampColorUVModeFoldOut", NBShaderFlags.FLAG_BIT_UVMODE_POS_0_RAMP_COLOR_MAP, 0, () => Content("颜色映射黑白图UV来源"), "_RampColorMap", true);
|
||||
new Vector2LineItem(rootItem, this, "_RampColorMapOffset", true, () => Content("颜色映射贴图偏移速度"));
|
||||
new VectorComponentItem(rootItem, this, "_RampColorMapOffset", 3, () => Content("颜色映射贴图旋转"), true, 0f, 360f);
|
||||
AddGradient(rootItem, this, "映射颜色", "_RampColorCount", "_RampColor", "_RampColorAlpha", hdr: true);
|
||||
new FeaturePopupItem(rootItem, this, "_RampColorBlendMode", () => Content("Ramp颜色混合模式"), BlendModeNames,
|
||||
property => rootItem.SyncService.ApplyToggleFlag(NBShaderFlags.FLAG_BIT_PARTICLE_RAMP_COLOR_BLEND_ADD, property.floatValue > 0.5f));
|
||||
new ColorItem(rootItem, this, "_RampColorBlendColor", () => Content("颜色映射叠加颜色"));
|
||||
InitTriggerByChild();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 45dcb7c7a750440a99a8d5017d41fa22
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using NBShader;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBShaderEditor
|
||||
{
|
||||
internal sealed class SharedUVFeatureItem : FeatureToggleFoldOutItem
|
||||
{
|
||||
public SharedUVFeatureItem(NBShaderRootItem rootItem, ShaderGUIItem parentItem)
|
||||
: base(rootItem, parentItem, "_SharedUVBlockFoldOut", "_SharedUVToggle", "公共UV", keyword: "_SHARED_UV")
|
||||
{
|
||||
new UVModeSelectItem(rootItem, this, "_SharedUVModeFoldOut", NBShaderFlags.FLAG_BIT_UVMODE_POS_0_SHAREDUV, 0, () => Content("公共UV来源"), forceEnable: true);
|
||||
new Vector2LineItem(rootItem, this, "_SharedUV_ST", true, () => Content("公共UV Tiling"));
|
||||
new Vector2LineItem(rootItem, this, "_SharedUV_ST", false, () => Content("公共UV Offset"));
|
||||
new Vector2LineItem(rootItem, this, "_SharedUV_Vec", true, () => Content("公共UV偏移速度"));
|
||||
new VectorComponentItem(rootItem, this, "_SharedUV_Vec", 2, () => Content("旋转"), false);
|
||||
new VectorComponentItem(rootItem, this, "_SharedUV_Vec", 3, () => Content("旋转速度"), false);
|
||||
new CustomDataSelectItem(rootItem, this, NBShaderFlags.FLAGBIT_POS_3_CUSTOMDATA_SHARED_UV_OFFSET_X, 3, () => Content("公共UV X轴偏移自定义曲线"));
|
||||
new CustomDataSelectItem(rootItem, this, NBShaderFlags.FLAGBIT_POS_3_CUSTOMDATA_SHARED_UV_OFFSET_Y, 3, () => Content("公共UV Y轴偏移自定义曲线"));
|
||||
InitTriggerByChild();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dece160aaa274dd79c1e771fed834249
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,428 @@
|
||||
using System;
|
||||
using NBShader;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBShaderEditor
|
||||
{
|
||||
internal sealed class VatFeatureItem : FeatureToggleFoldOutItem
|
||||
{
|
||||
private static readonly string[] VatModeNames = { "Houdini", "TyFlow" };
|
||||
private static readonly string[] HoudiniVatSubModeNames =
|
||||
{
|
||||
"SoftBody (Deformation)",
|
||||
"RigidBody (Pieces)",
|
||||
"Dynamic Remeshing (Lookup)",
|
||||
"Particle Sprites (Billboard)"
|
||||
};
|
||||
private static readonly string[] TyFlowVatSubModeNames =
|
||||
{
|
||||
"Absolute positions",
|
||||
"Relative offsets",
|
||||
"Skin (R)",
|
||||
"Skin (PR)",
|
||||
"Skin (PRSAVE)",
|
||||
"Skin (PRSXYZ)"
|
||||
};
|
||||
|
||||
public VatFeatureItem(NBShaderRootItem rootItem, ShaderGUIItem parentItem)
|
||||
: base(rootItem, parentItem, "_VATBlockFoldOut", "_VAT_Toggle", "VAT顶点动画图", keyword: "_VAT", onValueChanged: rootItem.SyncService.ApplyVatEnabled)
|
||||
{
|
||||
new FeaturePopupItem(rootItem, this, "_VATMode", () => Content("VAT模式"), VatModeNames, _ => rootItem.SyncService.SyncMaterialState());
|
||||
Func<bool> isHoudini = () => IsPropertyMode(rootItem, "_VATMode", (int)VATMode.Houdini);
|
||||
Func<bool> isTyflow = () => IsPropertyMode(rootItem, "_VATMode", (int)VATMode.Tyflow);
|
||||
Func<bool> hasVatFrameCustomData = () => IsVatFrameCustomDataVisible(rootItem);
|
||||
ShaderGUIFloatItem floatItem;
|
||||
|
||||
new FeaturePopupItem(rootItem, this, "_HoudiniVATSubMode", () => Content("Houdini VAT Sub Mode"), HoudiniVatSubModeNames,
|
||||
_ => rootItem.SyncService.SyncMaterialState(), isHoudini);
|
||||
new HelpBoxItem(rootItem, this, () => Text("feature.vat.houdiniUnsupportedParticle.message", "该 Houdini VAT 类型需要 Mesh 多 UV 数据,不支持 ParticleSystem VertexStream 模式。"), MessageType.Warning,
|
||||
() => isHoudini() && HasUnsupportedHoudiniParticleMode(rootItem));
|
||||
new SectionLabelItem(rootItem, this, () => Content("Playback"), isHoudini);
|
||||
new ToggleItem(rootItem, this, "_B_autoPlayback", () => Content("Auto Playback"), isVisible: isHoudini);
|
||||
floatItem = new ShaderGUIFloatItem(rootItem, this, () => isHoudini() && ShouldDrawWhenFloatOff(rootItem, "_B_autoPlayback"))
|
||||
{
|
||||
PropertyName = "_displayFrame",
|
||||
GuiContent = Content("Display Frame")
|
||||
};
|
||||
floatItem.InitTriggerByChild();
|
||||
new VatFrameCustomDataItem(rootItem, this, () => Content("VAT Frame CustomData"), () => isHoudini() && hasVatFrameCustomData(), hasVatFrameCustomData);
|
||||
floatItem = new ShaderGUIFloatItem(rootItem, this, isHoudini)
|
||||
{
|
||||
PropertyName = "_gameTimeAtFirstFrame",
|
||||
GuiContent = Content("Game Time at First Frame")
|
||||
};
|
||||
floatItem.InitTriggerByChild();
|
||||
floatItem = new ShaderGUIFloatItem(rootItem, this, isHoudini)
|
||||
{
|
||||
PropertyName = "_playbackSpeed",
|
||||
GuiContent = Content("Playback Speed")
|
||||
};
|
||||
floatItem.InitTriggerByChild();
|
||||
floatItem = new ShaderGUIFloatItem(rootItem, this, isHoudini)
|
||||
{
|
||||
PropertyName = "_houdiniFPS",
|
||||
GuiContent = Content("Houdini FPS")
|
||||
};
|
||||
floatItem.InitTriggerByChild();
|
||||
new ToggleItem(rootItem, this, "_B_interpolate", () => Content("Interframe Interpolation"), isVisible: isHoudini);
|
||||
new ToggleItem(rootItem, this, "_animateFirstFrame", () => Content("Animate First Frame"), isVisible: () => isHoudini() && IsPropertyMode(rootItem, "_HoudiniVATSubMode", 1));
|
||||
floatItem = new ShaderGUIFloatItem(rootItem, this, isHoudini)
|
||||
{
|
||||
PropertyName = "_frameCount",
|
||||
GuiContent = Content("Frame Count")
|
||||
};
|
||||
floatItem.InitTriggerByChild();
|
||||
|
||||
new SectionLabelItem(rootItem, this, () => Content("Bounds Metadata"), isHoudini);
|
||||
floatItem = new ShaderGUIFloatItem(rootItem, this, isHoudini)
|
||||
{
|
||||
PropertyName = "_boundMinX",
|
||||
GuiContent = Content("Bound Min X")
|
||||
};
|
||||
floatItem.InitTriggerByChild();
|
||||
floatItem = new ShaderGUIFloatItem(rootItem, this, isHoudini)
|
||||
{
|
||||
PropertyName = "_boundMinY",
|
||||
GuiContent = Content("Bound Min Y")
|
||||
};
|
||||
floatItem.InitTriggerByChild();
|
||||
floatItem = new ShaderGUIFloatItem(rootItem, this, isHoudini)
|
||||
{
|
||||
PropertyName = "_boundMinZ",
|
||||
GuiContent = Content("Bound Min Z")
|
||||
};
|
||||
floatItem.InitTriggerByChild();
|
||||
floatItem = new ShaderGUIFloatItem(rootItem, this, isHoudini)
|
||||
{
|
||||
PropertyName = "_boundMaxX",
|
||||
GuiContent = Content("Bound Max X")
|
||||
};
|
||||
floatItem.InitTriggerByChild();
|
||||
floatItem = new ShaderGUIFloatItem(rootItem, this, isHoudini)
|
||||
{
|
||||
PropertyName = "_boundMaxY",
|
||||
GuiContent = Content("Bound Max Y")
|
||||
};
|
||||
floatItem.InitTriggerByChild();
|
||||
floatItem = new ShaderGUIFloatItem(rootItem, this, isHoudini)
|
||||
{
|
||||
PropertyName = "_boundMaxZ",
|
||||
GuiContent = Content("Bound Max Z")
|
||||
};
|
||||
floatItem.InitTriggerByChild();
|
||||
|
||||
new SectionLabelItem(rootItem, this, () => Content("Textures"), isHoudini);
|
||||
new TextureItem(rootItem, this, "_posTexture", () => Content("Position Texture"), drawScaleOffset: false, isVisible: isHoudini);
|
||||
new TextureItem(rootItem, this, "_posTexture2", () => Content("Position Texture 2"), drawScaleOffset: false,
|
||||
isVisible: () => isHoudini() && ShouldDrawWhenFloatOn(rootItem, "_B_LOAD_POS_TWO_TEX"));
|
||||
new TextureItem(rootItem, this, "_rotTexture", () => Content("Rotation Texture"), drawScaleOffset: false,
|
||||
isVisible: () => isHoudini() && ShouldDrawHoudiniRotationTexture(rootItem));
|
||||
new TextureItem(rootItem, this, "_colTexture", () => Content("Color Texture"), drawScaleOffset: false,
|
||||
isVisible: () => isHoudini() && ShouldDrawWhenFloatOn(rootItem, "_B_LOAD_COL_TEX"));
|
||||
new TextureItem(rootItem, this, "_lookupTable", () => Content("Lookup Table"), drawScaleOffset: false,
|
||||
isVisible: () => isHoudini() && (IsPropertyMode(rootItem, "_HoudiniVATSubMode", 2) || ShouldDrawWhenFloatOn(rootItem, "_B_LOAD_LOOKUP_TABLE")));
|
||||
|
||||
new SectionLabelItem(rootItem, this, () => Content("Scale"), () => isHoudini() && IsPropertyMode(rootItem, "_HoudiniVATSubMode", 1, 3));
|
||||
floatItem = new ShaderGUIFloatItem(rootItem, this, () => isHoudini() && IsPropertyMode(rootItem, "_HoudiniVATSubMode", 1, 3))
|
||||
{
|
||||
PropertyName = "_globalPscaleMul",
|
||||
GuiContent = Content("Global Piece Scale Multiplier")
|
||||
};
|
||||
floatItem.InitTriggerByChild();
|
||||
new ToggleItem(rootItem, this, "_B_pscaleAreInPosA", () => Content("Piece Scales in Position Alpha"), isVisible: () => isHoudini() && IsPropertyMode(rootItem, "_HoudiniVATSubMode", 1, 3));
|
||||
|
||||
new SectionLabelItem(rootItem, this, () => Content("Particle Sprite"), () => isHoudini() && IsPropertyMode(rootItem, "_HoudiniVATSubMode", 3));
|
||||
floatItem = new ShaderGUIFloatItem(rootItem, this, () => isHoudini() && IsPropertyMode(rootItem, "_HoudiniVATSubMode", 3))
|
||||
{
|
||||
PropertyName = "_widthBaseScale",
|
||||
GuiContent = Content("Width Base Scale")
|
||||
};
|
||||
floatItem.InitTriggerByChild();
|
||||
floatItem = new ShaderGUIFloatItem(rootItem, this, () => isHoudini() && IsPropertyMode(rootItem, "_HoudiniVATSubMode", 3))
|
||||
{
|
||||
PropertyName = "_heightBaseScale",
|
||||
GuiContent = Content("Height Base Scale")
|
||||
};
|
||||
floatItem.InitTriggerByChild();
|
||||
new ToggleItem(rootItem, this, "_B_hideOverlappingOrigin", () => Content("Hide Overlapping Origin"), isVisible: () => isHoudini() && IsPropertyMode(rootItem, "_HoudiniVATSubMode", 3));
|
||||
floatItem = new ShaderGUIFloatItem(rootItem, this, () => isHoudini() && IsPropertyMode(rootItem, "_HoudiniVATSubMode", 3) && ShouldDrawWhenFloatOn(rootItem, "_B_hideOverlappingOrigin"))
|
||||
{
|
||||
PropertyName = "_originRadius",
|
||||
GuiContent = Content("Origin Effective Radius")
|
||||
};
|
||||
floatItem.InitTriggerByChild();
|
||||
new ToggleItem(rootItem, this, "_B_CAN_SPIN", () => Content("Particles Can Spin"), isVisible: () => isHoudini() && IsPropertyMode(rootItem, "_HoudiniVATSubMode", 3));
|
||||
new ToggleItem(rootItem, this, "_B_spinFromHeading", () => Content("Compute Spin from Heading"), isVisible: () => isHoudini() && IsPropertyMode(rootItem, "_HoudiniVATSubMode", 3) && ShouldDrawWhenFloatOn(rootItem, "_B_CAN_SPIN"));
|
||||
floatItem = new ShaderGUIFloatItem(rootItem, this, () => isHoudini() && IsPropertyMode(rootItem, "_HoudiniVATSubMode", 3) && ShouldDrawWhenFloatOn(rootItem, "_B_CAN_SPIN") && ShouldDrawWhenFloatOff(rootItem, "_B_spinFromHeading"))
|
||||
{
|
||||
PropertyName = "_spinPhase",
|
||||
GuiContent = Content("Particle Spin Phase")
|
||||
};
|
||||
floatItem.InitTriggerByChild();
|
||||
floatItem = new ShaderGUIFloatItem(rootItem, this, () => isHoudini() && IsPropertyMode(rootItem, "_HoudiniVATSubMode", 3) && ShouldDrawWhenFloatOn(rootItem, "_B_CAN_SPIN"))
|
||||
{
|
||||
PropertyName = "_scaleByVelAmount",
|
||||
GuiContent = Content("Scale by Velocity Amount")
|
||||
};
|
||||
floatItem.InitTriggerByChild();
|
||||
floatItem = new ShaderGUIFloatItem(rootItem, this, () => isHoudini() && IsPropertyMode(rootItem, "_HoudiniVATSubMode", 3))
|
||||
{
|
||||
PropertyName = "_particleTexUScale",
|
||||
GuiContent = Content("Particle Texture U Scale")
|
||||
};
|
||||
floatItem.InitTriggerByChild();
|
||||
floatItem = new ShaderGUIFloatItem(rootItem, this, () => isHoudini() && IsPropertyMode(rootItem, "_HoudiniVATSubMode", 3))
|
||||
{
|
||||
PropertyName = "_particleTexVScale",
|
||||
GuiContent = Content("Particle Texture V Scale")
|
||||
};
|
||||
floatItem.InitTriggerByChild();
|
||||
|
||||
new SectionLabelItem(rootItem, this, () => Content("Flags"), isHoudini);
|
||||
new ToggleItem(rootItem, this, "_B_LOAD_POS_TWO_TEX", () => Content("Positions Require Two Textures"), isVisible: isHoudini);
|
||||
new ToggleItem(rootItem, this, "_B_UNLOAD_ROT_TEX", () => Content("Use Compressed Normals (no rotTex)"), isVisible: () => isHoudini() && ShouldDrawCompressedNormalsToggle(rootItem));
|
||||
new ToggleItem(rootItem, this, "_B_LOAD_COL_TEX", () => Content("Load Color Texture"), isVisible: isHoudini);
|
||||
new ToggleItem(rootItem, this, "_B_LOAD_LOOKUP_TABLE", () => Content("Load Lookup Table"), isVisible: () => isHoudini() && IsPropertyMode(rootItem, "_HoudiniVATSubMode", 2));
|
||||
|
||||
new TextureItem(rootItem, this, "_VATTex", () => Content("VAT texture"), drawScaleOffset: false, isVisible: isTyflow);
|
||||
floatItem = new ShaderGUIFloatItem(rootItem, this, isTyflow)
|
||||
{
|
||||
PropertyName = "_ImportScale",
|
||||
GuiContent = Content("ImportScale")
|
||||
};
|
||||
floatItem.InitTriggerByChild();
|
||||
new FeaturePopupItem(rootItem, this, "_TyFlowVATSubMode", () => Content("TyFlow VAT Sub Mode"), TyFlowVatSubModeNames,
|
||||
_ => rootItem.SyncService.SyncMaterialState(), isTyflow);
|
||||
new HelpBoxItem(rootItem, this, () => Text("feature.vat.tyflowUnsupportedParticle.message", "该 TyFlow VAT 类型需要 Mesh 多 UV 数据,不支持 ParticleSystem VertexStream 模式。"), MessageType.Warning,
|
||||
() => isTyflow() && HasUnsupportedTyflowParticleMode(rootItem));
|
||||
new HelpBoxItem(rootItem, this, () => Text("feature.vat.tyflowUv2Conflict.message", "TyFlow VAT uses UV2 (TEXCOORD0.zw) as vertexIndex / vertexCount in ParticleSystem mode. Flipbook blending or Special UV (UV2) conflicts with it; VAT takes priority."), MessageType.Warning,
|
||||
() => isTyflow() && !HasUnsupportedTyflowParticleMode(rootItem) && HasTyflowParticleUV2Conflict(rootItem));
|
||||
new ToggleItem(rootItem, this, "_DeformingSkin", () => Content("Deforming skin"), isVisible: isTyflow);
|
||||
floatItem = new ShaderGUIFloatItem(rootItem, this, isTyflow)
|
||||
{
|
||||
PropertyName = "_SkinBoneCount",
|
||||
GuiContent = Content("Skin bone count")
|
||||
};
|
||||
floatItem.InitTriggerByChild();
|
||||
new ToggleItem(rootItem, this, "_RGBAEncoded", () => Content("RGBA encoded"), isVisible: isTyflow);
|
||||
new ToggleItem(rootItem, this, "_RGBAHalf", () => Content("RGBA half"), isVisible: isTyflow);
|
||||
new ToggleItem(rootItem, this, "_LinearToGamma", () => Content("Gamma correction"), isVisible: isTyflow);
|
||||
new ToggleItem(rootItem, this, "_VATIncludesNormals", () => Content("VAT includes normals"), isVisible: isTyflow);
|
||||
floatItem = new ShaderGUIFloatItem(rootItem, this, isTyflow)
|
||||
{
|
||||
PropertyName = "_Frame",
|
||||
GuiContent = Content("Frame")
|
||||
};
|
||||
floatItem.InitTriggerByChild();
|
||||
new VatFrameCustomDataItem(rootItem, this, () => Content("VAT Frame CustomData"), () => isTyflow() && hasVatFrameCustomData(), hasVatFrameCustomData);
|
||||
floatItem = new ShaderGUIFloatItem(rootItem, this, isTyflow)
|
||||
{
|
||||
PropertyName = "_Frames",
|
||||
GuiContent = Content("Frames")
|
||||
};
|
||||
floatItem.InitTriggerByChild();
|
||||
new ToggleItem(rootItem, this, "_FrameInterpolation", () => Content("Frame interpolation"), isVisible: isTyflow);
|
||||
new ToggleItem(rootItem, this, "_Loop", () => Content("Loop"), isVisible: isTyflow);
|
||||
new ToggleItem(rootItem, this, "_InterpolateLoop", () => Content("Interpolate loop"), isVisible: isTyflow);
|
||||
new ToggleItem(rootItem, this, "_Autoplay", () => Content("Autoplay"), isVisible: isTyflow);
|
||||
floatItem = new ShaderGUIFloatItem(rootItem, this, isTyflow)
|
||||
{
|
||||
PropertyName = "_AutoplaySpeed",
|
||||
GuiContent = Content("AutoplaySpeed")
|
||||
};
|
||||
floatItem.InitTriggerByChild();
|
||||
InitTriggerByChild();
|
||||
}
|
||||
|
||||
private static bool ShouldDrawWhenFloatOn(NBShaderRootItem rootItem, string propertyName)
|
||||
{
|
||||
if (!rootItem.PropertyInfoDic.TryGetValue(propertyName, out ShaderPropertyInfo info))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return info.Property.hasMixedValue || info.Property.floatValue > 0.5f;
|
||||
}
|
||||
|
||||
private static bool ShouldDrawWhenFloatOff(NBShaderRootItem rootItem, string propertyName)
|
||||
{
|
||||
if (!rootItem.PropertyInfoDic.TryGetValue(propertyName, out ShaderPropertyInfo info))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return info.Property.hasMixedValue || info.Property.floatValue <= 0.5f;
|
||||
}
|
||||
|
||||
private static bool TryGetPropertyMode(NBShaderRootItem rootItem, string propertyName, out int mode)
|
||||
{
|
||||
mode = 0;
|
||||
if (!rootItem.PropertyInfoDic.TryGetValue(propertyName, out ShaderPropertyInfo info) ||
|
||||
info.Property.hasMixedValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
mode = Mathf.RoundToInt(info.Property.floatValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool ShouldDrawHoudiniRotationTexture(NBShaderRootItem rootItem)
|
||||
{
|
||||
return TryGetPropertyMode(rootItem, "_HoudiniVATSubMode", out int subMode) &&
|
||||
subMode != 3 &&
|
||||
ShouldDrawWhenFloatOff(rootItem, "_B_UNLOAD_ROT_TEX");
|
||||
}
|
||||
|
||||
private static bool ShouldDrawCompressedNormalsToggle(NBShaderRootItem rootItem)
|
||||
{
|
||||
return TryGetPropertyMode(rootItem, "_HoudiniVATSubMode", out int subMode) && subMode != 3;
|
||||
}
|
||||
|
||||
private static bool IsVatFrameCustomDataVisible(NBShaderRootItem rootItem)
|
||||
{
|
||||
return IsAnyHoudiniParticleModeEnabled(rootItem) || IsAnyTyflowParticleModeEnabled(rootItem);
|
||||
}
|
||||
|
||||
private static bool HasTyflowParticleUV2Conflict(NBShaderRootItem rootItem)
|
||||
{
|
||||
if (rootItem.Mats == null || rootItem.ShaderFlags == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int count = Mathf.Min(rootItem.Mats.Count, rootItem.ShaderFlags.Count);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
Material mat = rootItem.Mats[i];
|
||||
if (!IsTyflowParticleModeEnabled(mat) ||
|
||||
!(rootItem.ShaderFlags[i] is NBShaderFlags flags))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool flipbook = mat.IsKeywordEnabled("_FLIPBOOKBLENDING_ON");
|
||||
bool specialUVUsesUV2 = flags.CheckIsUVModeOn(NBShaderFlags.UVMode.SpecialUVChannel) &&
|
||||
!flags.CheckFlagBits(NBShaderFlags.FLAG_BIT_PARTICLE_1_USE_TEXCOORD2, index: 1);
|
||||
if (flipbook || specialUVUsesUV2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsAnyHoudiniParticleModeEnabled(NBShaderRootItem rootItem)
|
||||
{
|
||||
if (rootItem.Mats == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rootItem.Mats.Count; i++)
|
||||
{
|
||||
if (IsHoudiniParticleModeEnabled(rootItem.Mats[i]))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsAnyTyflowParticleModeEnabled(NBShaderRootItem rootItem)
|
||||
{
|
||||
if (rootItem.Mats == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rootItem.Mats.Count; i++)
|
||||
{
|
||||
if (IsTyflowParticleModeEnabled(rootItem.Mats[i]))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool HasUnsupportedHoudiniParticleMode(NBShaderRootItem rootItem)
|
||||
{
|
||||
if (rootItem.Mats == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rootItem.Mats.Count; i++)
|
||||
{
|
||||
Material mat = rootItem.Mats[i];
|
||||
if (IsHoudiniParticleModeEnabled(mat) &&
|
||||
GetMaterialInt(mat, "_HoudiniVATSubMode", 0) == 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool HasUnsupportedTyflowParticleMode(NBShaderRootItem rootItem)
|
||||
{
|
||||
if (rootItem.Mats == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rootItem.Mats.Count; i++)
|
||||
{
|
||||
Material mat = rootItem.Mats[i];
|
||||
if (IsVatParticleMode(mat, (int)VATMode.Tyflow) &&
|
||||
GetMaterialInt(mat, "_TyFlowVATSubMode", 0) >= 2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsHoudiniParticleModeEnabled(Material mat)
|
||||
{
|
||||
return IsVatParticleMode(mat, (int)VATMode.Houdini);
|
||||
}
|
||||
|
||||
private static bool IsTyflowParticleModeEnabled(Material mat)
|
||||
{
|
||||
return IsVatParticleMode(mat, (int)VATMode.Tyflow) &&
|
||||
(!mat.HasProperty("_TyFlowVATSubMode") || Mathf.RoundToInt(mat.GetFloat("_TyFlowVATSubMode")) <= 1);
|
||||
}
|
||||
|
||||
private static bool IsVatParticleMode(Material mat, int vatMode)
|
||||
{
|
||||
if (mat == null ||
|
||||
!mat.HasProperty("_VAT_Toggle") ||
|
||||
!mat.HasProperty("_VATMode") ||
|
||||
!mat.HasProperty("_MeshSourceMode") ||
|
||||
mat.GetFloat("_VAT_Toggle") <= 0.5f ||
|
||||
Mathf.RoundToInt(mat.GetFloat("_VATMode")) != vatMode)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
MeshSourceMode meshSourceMode = (MeshSourceMode)Mathf.RoundToInt(mat.GetFloat("_MeshSourceMode"));
|
||||
return meshSourceMode == MeshSourceMode.Particle ||
|
||||
meshSourceMode == MeshSourceMode.UIParticle;
|
||||
}
|
||||
|
||||
private static int GetMaterialInt(Material mat, string propertyName, int defaultValue)
|
||||
{
|
||||
return mat != null && mat.HasProperty(propertyName)
|
||||
? Mathf.RoundToInt(mat.GetFloat(propertyName))
|
||||
: defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 744c08c430cd42fda37db42e777c3c84
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using NBShader;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBShaderEditor
|
||||
{
|
||||
internal sealed class VertexOffsetFeatureItem : FeatureToggleFoldOutItem
|
||||
{
|
||||
public VertexOffsetFeatureItem(NBShaderRootItem rootItem, ShaderGUIItem parentItem)
|
||||
: base(rootItem, parentItem, "_VertexOffsetBlockFoldOut", "_VertexOffset_Toggle", "顶点偏移", keyword: "_VERTEX_OFFSET")
|
||||
{
|
||||
new NBShaderKeywordToggleItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_NB_Debug_VertexOffset",
|
||||
"NB_DEBUG_VERTEX_OFFSET",
|
||||
() => Content("顶点偏移方向测试"),
|
||||
isVisible: null);
|
||||
AddTextureWithWrap(rootItem, this, "_VertexOffset_Map", "顶点偏移贴图", NBShaderFlags.FLAG_BIT_WRAPMODE_VERTEXOFFSETMAP);
|
||||
new UVModeSelectItem(rootItem, this, "_VertexOffsetUVModeFoldOut", NBShaderFlags.FLAG_BIT_UVMODE_POS_0_VERTEX_OFFSET_MAP, 0, () => Content("顶点偏移贴图UV来源"), "_VertexOffset_Map");
|
||||
new CustomDataSelectItem(rootItem, this, NBShaderFlags.FLAGBIT_POS_1_CUSTOMDATA_VERTEX_OFFSET_X, 1, () => Content("顶点扰动X轴偏移自定义曲线"));
|
||||
new CustomDataSelectItem(rootItem, this, NBShaderFlags.FLAGBIT_POS_1_CUSTOMDATA_VERTEX_OFFSET_Y, 1, () => Content("顶点扰动Y轴偏移自定义曲线"));
|
||||
new Vector2LineItem(rootItem, this, "_VertexOffset_Vec", true, () => Content("顶点偏移动画"));
|
||||
new VectorComponentItem(rootItem, this, "_VertexOffset_Vec", 2, () => Content("顶点偏移强度"), false);
|
||||
new CustomDataSelectItem(rootItem, this, NBShaderFlags.FLAGBIT_POS_1_CUSTOMDATA_VERTEXOFFSET_INTENSITY, 1, () => Content("顶点扰动强度自定义曲线"));
|
||||
new ToggleItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_VertexOffset_StartFromZero",
|
||||
() => Content("顶点偏移从零开始"),
|
||||
enabled => rootItem.SyncService.ApplyToggleFlag(NBShaderFlags.FLAG_BIT_PARTICLE_1_VERTEXOFFSET_START_FROM_ZERO, enabled, 1));
|
||||
new ToggleItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_VertexOffset_NormalDir_Toggle",
|
||||
() => Content("顶点偏移使用法线方向"),
|
||||
enabled => rootItem.SyncService.ApplyToggleFlag(NBShaderFlags.FLAG_BIT_PARTICLE_VERTEX_OFFSET_NORMAL_DIR, enabled));
|
||||
Func<bool> showCustomDirection = () => IsPropertyOff(rootItem, "_VertexOffset_NormalDir_Toggle");
|
||||
new VectorComponentItem(rootItem, this, "_VertexOffset_CustomDir", 0, () => Content("顶点偏移本地方向X"), false, isVisible: showCustomDirection);
|
||||
new VectorComponentItem(rootItem, this, "_VertexOffset_CustomDir", 1, () => Content("顶点偏移本地方向Y"), false, isVisible: showCustomDirection);
|
||||
new VectorComponentItem(rootItem, this, "_VertexOffset_CustomDir", 2, () => Content("顶点偏移本地方向Z"), false, isVisible: showCustomDirection);
|
||||
|
||||
PropertyToggleBlockItem maskBlock = ToggleBlock(rootItem, "_VertexOffsetMaskBlockFoldOut", "_VertexOffset_Mask_Toggle", "顶点偏移遮罩",
|
||||
parent: this, keyword: "_VERTEX_OFFSET_MASKMAP");
|
||||
AddTextureWithWrap(rootItem, maskBlock, "_VertexOffset_MaskMap", "顶点偏移遮罩图", NBShaderFlags.FLAG_BIT_WRAPMODE_VERTEXOFFSET_MASKMAP);
|
||||
new UVModeSelectItem(rootItem, maskBlock, "_VertexOffsetMaskUVModeFoldOut", NBShaderFlags.FLAG_BIT_UVMODE_POS_0_VERTEX_OFFSET_MASKMAP, 0, () => Content("顶点偏移遮罩图UV来源"), "_VertexOffset_MaskMap");
|
||||
new CustomDataSelectItem(rootItem, maskBlock, NBShaderFlags.FLAGBIT_POS_3_CUSTOMDATA_VERTEX_OFFSET_MASK_X, 3, () => Content("顶点扰动遮罩X轴偏移自定义曲线"));
|
||||
new CustomDataSelectItem(rootItem, maskBlock, NBShaderFlags.FLAGBIT_POS_3_CUSTOMDATA_VERTEX_OFFSET_MASK_Y, 3, () => Content("顶点扰动遮罩Y轴偏移自定义曲线"));
|
||||
new Vector2LineItem(rootItem, maskBlock, "_VertexOffset_MaskMap_Vec", true, () => Content("顶点偏移遮罩动画"));
|
||||
new VectorComponentItem(rootItem, maskBlock, "_VertexOffset_MaskMap_Vec", 2, () => Content("顶点偏移遮罩强度"), true);
|
||||
InitTriggerByChild();
|
||||
}
|
||||
|
||||
private static bool IsPropertyOff(NBShaderRootItem rootItem, string propertyName)
|
||||
{
|
||||
return rootItem.PropertyInfoDic.TryGetValue(propertyName, out ShaderPropertyInfo info) &&
|
||||
!info.Property.hasMixedValue &&
|
||||
info.Property.floatValue <= 0.5f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5507463f5ee64c358b78250126100a8d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,358 @@
|
||||
using NBShader;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBShaderEditor
|
||||
{
|
||||
public enum FxLightMode
|
||||
{
|
||||
UnLit = 0,
|
||||
BlinnPhong = 1,
|
||||
HalfLambert = 2,
|
||||
PBR = 3,
|
||||
SixWay = 4,
|
||||
UnKnownOrMixedValue = -1
|
||||
}
|
||||
|
||||
public class LightBigBlockItem : BigBlockItem
|
||||
{
|
||||
private readonly NBShaderRootItem _nbRootItem;
|
||||
private readonly FxLightModePopupItem _lightModeItem;
|
||||
private readonly ToggleItem _specularToggleItem;
|
||||
private readonly ColorItem _specularColorItem;
|
||||
private readonly VectorComponentItem _specularSmoothnessItem;
|
||||
private readonly VectorComponentItem _pbrMetallicItem;
|
||||
private readonly VectorComponentItem _pbrSmoothnessItem;
|
||||
private readonly PropertyToggleBlockItem _bumpBlock;
|
||||
private readonly PropertyToggleBlockItem _matCapBlock;
|
||||
private readonly TextureItem _sixWayPositiveItem;
|
||||
private readonly TextureItem _sixWayNegativeItem;
|
||||
private readonly ToggleItem _sixWayAbsorptionToggleItem;
|
||||
private readonly VectorComponentItem _sixWayAbsorptionStrengthItem;
|
||||
private readonly TextureItem _sixWayEmissionRampItem;
|
||||
private readonly VectorComponentItem _sixWayEmissionPowItem;
|
||||
private readonly ColorItem _sixWayEmissionColorItem;
|
||||
|
||||
public LightBigBlockItem(NBShaderRootItem rootItem, ShaderGUIItem parentItem)
|
||||
: base(
|
||||
rootItem,
|
||||
parentItem,
|
||||
"_LightBigBlockItemFoldOut",
|
||||
() => Content("block.light", "Light", "Normal, MatCap and light mode controls"))
|
||||
{
|
||||
_nbRootItem = rootItem;
|
||||
_lightModeItem = new FxLightModePopupItem(rootItem, this);
|
||||
|
||||
_specularToggleItem = new NBShaderKeywordToggleItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_BlinnPhongSpecularToggle",
|
||||
"_SPECULAR_COLOR",
|
||||
() => Content("light.specular.toggle", "Specular"),
|
||||
isVisible: () => IsBlinnOrHalf(rootItem));
|
||||
|
||||
_specularColorItem = new ColorItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_SpecularColor",
|
||||
() => Content("light.specular.color", "Specular Color"),
|
||||
() => IsToggleOn(rootItem, "_BlinnPhongSpecularToggle") && IsBlinnOrHalf(rootItem));
|
||||
|
||||
_specularSmoothnessItem = new VectorComponentItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_MaterialInfo",
|
||||
1,
|
||||
() => Content("light.specular.smoothness", "Smoothness"),
|
||||
true,
|
||||
0f,
|
||||
1f,
|
||||
() => IsToggleOn(rootItem, "_BlinnPhongSpecularToggle") && IsBlinnOrHalf(rootItem));
|
||||
|
||||
_pbrMetallicItem = new VectorComponentItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_MaterialInfo",
|
||||
0,
|
||||
() => Content("light.pbr.metallic", "Metallic"),
|
||||
true,
|
||||
0f,
|
||||
1f,
|
||||
() => rootItem.Context.FxLightMode == FxLightMode.PBR);
|
||||
|
||||
_pbrSmoothnessItem = new VectorComponentItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_MaterialInfo",
|
||||
1,
|
||||
() => Content("light.pbr.smoothness", "Smoothness"),
|
||||
true,
|
||||
0f,
|
||||
1f,
|
||||
() => rootItem.Context.FxLightMode == FxLightMode.PBR);
|
||||
|
||||
_bumpBlock = new PropertyToggleBlockItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_BumpToggleFoldOut",
|
||||
"_BumpMapToggle",
|
||||
() => Content("light.bump.toggle", "Normal Map"),
|
||||
keyword: "_NORMALMAP",
|
||||
isVisible: () => rootItem.Context.FxLightMode != FxLightMode.SixWay);
|
||||
|
||||
new TextureItem(
|
||||
rootItem,
|
||||
_bumpBlock,
|
||||
"_BumpTex",
|
||||
() => Content("light.bump.texture", "Normal Map"),
|
||||
drawScaleOffset: true);
|
||||
TextureRelatedFoldOutItem bumpTexRelatedFoldOut = new TextureRelatedFoldOutItem(
|
||||
rootItem,
|
||||
_bumpBlock,
|
||||
"_BumpTexFoldOut",
|
||||
"_BumpTex",
|
||||
() => Content("light.bump.related", "Normal Map Related"));
|
||||
new WrapModeItem(rootItem, bumpTexRelatedFoldOut, NBShaderFlags.FLAG_BIT_WRAPMODE_BUMPTEX, () => Content("light.bump.wrap", "Normal Map Wrap"));
|
||||
new UVModeSelectItem(
|
||||
rootItem,
|
||||
bumpTexRelatedFoldOut,
|
||||
"_BumpUVModeFoldOut",
|
||||
NBShaderFlags.FLAG_BIT_UVMODE_POS_0_BUMPMAP,
|
||||
0,
|
||||
() => Content("light.bump.uvmode", "Normal Map UV Source"),
|
||||
"_BumpTex");
|
||||
new ToggleItem(
|
||||
rootItem,
|
||||
bumpTexRelatedFoldOut,
|
||||
"_BumpMapMaskMode",
|
||||
() => Content("light.bump.maskMode", "Normal Map Multi Channel"),
|
||||
enabled => rootItem.SyncService.ApplyToggleFlag(NBShaderFlags.FLAG_BIT_PARTICLE_NORMALMAP_MASK_MODE, enabled));
|
||||
ShaderGUISliderItem bumpScaleItem = new ShaderGUISliderItem(rootItem, bumpTexRelatedFoldOut)
|
||||
{
|
||||
PropertyName = "_BumpScale",
|
||||
GuiContent = Content("light.bump.scale", "Normal Strength"),
|
||||
RangePropertyName = "BumpScaleRangeVec"
|
||||
};
|
||||
bumpScaleItem.InitTriggerByChild();
|
||||
|
||||
_matCapBlock = new PropertyToggleBlockItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_MatCapFoldOut",
|
||||
"_MatCapToggle",
|
||||
() => Content("light.matcap.toggle", "MatCap"),
|
||||
keyword: "_MATCAP",
|
||||
isVisible: () => rootItem.Context.FxLightMode != FxLightMode.SixWay);
|
||||
new TextureItem(rootItem, _matCapBlock, "_MatCapTex", () => Content("light.matcap.texture", "MatCap Texture"), "_MatCapColor", false);
|
||||
new VectorComponentItem(rootItem, _matCapBlock, "_MatCapInfo", 0, () => Content("light.matcap.blend", "Add/Multiply Blend"), true, 0f, 1f);
|
||||
|
||||
_sixWayPositiveItem = new TextureItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_RigRTBk",
|
||||
() => Content("light.sixway.positive", "SixWay Positive"),
|
||||
drawScaleOffset: false,
|
||||
isVisible: IsSixWay);
|
||||
|
||||
_sixWayNegativeItem = new TextureItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_RigLBtF",
|
||||
() => Content("light.sixway.negative", "SixWay Negative"),
|
||||
drawScaleOffset: false,
|
||||
isVisible: IsSixWay);
|
||||
|
||||
_sixWayAbsorptionToggleItem = new NBShaderKeywordToggleItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_SixWayColorAbsorptionToggle",
|
||||
"VFX_SIX_WAY_ABSORPTION",
|
||||
() => Content("light.sixway.absorption.toggle", "Light Color Absorption"),
|
||||
isVisible: IsSixWay);
|
||||
|
||||
_sixWayAbsorptionStrengthItem = new VectorComponentItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_SixWayInfo",
|
||||
0,
|
||||
() => Content("light.sixway.absorption.strength", "Absorption Strength"),
|
||||
true,
|
||||
0f,
|
||||
1f,
|
||||
() => IsSixWay() && IsToggleOn(rootItem, "_SixWayColorAbsorptionToggle"));
|
||||
|
||||
_sixWayEmissionRampItem = new TextureItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_SixWayEmissionRamp",
|
||||
() => Content("light.sixway.ramp", "SixWay Emission Ramp"),
|
||||
drawScaleOffset: false,
|
||||
afterDraw: SyncSixWayRampFlag,
|
||||
isVisible: IsSixWay);
|
||||
|
||||
_sixWayEmissionPowItem = new VectorComponentItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_SixWayInfo",
|
||||
1,
|
||||
() => Content("light.sixway.emissionPow", "SixWay Emission Pow"),
|
||||
false,
|
||||
isVisible: IsSixWay);
|
||||
|
||||
_sixWayEmissionColorItem = new ColorItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_SixWayEmissionColor",
|
||||
() => Content("light.sixway.color", "SixWay Emission Color"),
|
||||
IsSixWay);
|
||||
|
||||
InitTriggerByChild();
|
||||
}
|
||||
|
||||
public override void DrawBlock()
|
||||
{
|
||||
if (IsAnyLightModeAllowed())
|
||||
{
|
||||
_lightModeItem.OnGUI();
|
||||
}
|
||||
|
||||
if (IsTierAllowed("_SPECULAR_COLOR"))
|
||||
{
|
||||
_specularToggleItem.OnGUI();
|
||||
_specularColorItem.OnGUI();
|
||||
_specularSmoothnessItem.OnGUI();
|
||||
}
|
||||
|
||||
if (IsTierAllowed("_FX_LIGHT_MODE_PBR"))
|
||||
{
|
||||
_pbrMetallicItem.OnGUI();
|
||||
_pbrSmoothnessItem.OnGUI();
|
||||
}
|
||||
|
||||
if (IsTierAllowed("_NORMALMAP"))
|
||||
{
|
||||
_bumpBlock.OnGUI();
|
||||
}
|
||||
|
||||
if (IsTierAllowed("_MATCAP"))
|
||||
{
|
||||
_matCapBlock.OnGUI();
|
||||
}
|
||||
|
||||
if (IsTierAllowed("_FX_LIGHT_MODE_SIX_WAY"))
|
||||
{
|
||||
_sixWayPositiveItem.OnGUI();
|
||||
_sixWayNegativeItem.OnGUI();
|
||||
DrawSixWayWarning();
|
||||
_sixWayEmissionRampItem.OnGUI();
|
||||
_sixWayEmissionPowItem.OnGUI();
|
||||
_sixWayEmissionColorItem.OnGUI();
|
||||
}
|
||||
|
||||
if (IsTierAllowed("VFX_SIX_WAY_ABSORPTION"))
|
||||
{
|
||||
_sixWayAbsorptionToggleItem.OnGUI();
|
||||
_sixWayAbsorptionStrengthItem.OnGUI();
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsTierAllowed(string keyword)
|
||||
{
|
||||
return _nbRootItem.Context == null || _nbRootItem.Context.IsKeywordAllowed(keyword);
|
||||
}
|
||||
|
||||
private bool IsAnyLightModeAllowed()
|
||||
{
|
||||
return _nbRootItem.Context == null ||
|
||||
_nbRootItem.Context.IsAnyKeywordAllowed(
|
||||
"_FX_LIGHT_MODE_UNLIT",
|
||||
"_FX_LIGHT_MODE_BLINN_PHONG",
|
||||
"_FX_LIGHT_MODE_HALF_LAMBERT",
|
||||
"_FX_LIGHT_MODE_PBR",
|
||||
"_FX_LIGHT_MODE_SIX_WAY");
|
||||
}
|
||||
|
||||
private bool IsSixWay()
|
||||
{
|
||||
return _nbRootItem.Context.FxLightMode == FxLightMode.SixWay;
|
||||
}
|
||||
|
||||
private void DrawSixWayWarning()
|
||||
{
|
||||
if (!IsSixWay())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DrawLayoutHelpBox(
|
||||
NBShaderInspectorLocalization.GetInspectorText(
|
||||
"light.sixway.uvWarning.message",
|
||||
"六路UV跟随主贴图UV及颜色"),
|
||||
MessageType.Warning);
|
||||
}
|
||||
|
||||
private void SyncSixWayRampFlag(MaterialProperty rampProperty)
|
||||
{
|
||||
if (rampProperty.hasMixedValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_nbRootItem.SyncService.ApplyToggleFlag(
|
||||
NBShaderFlags.FLAG_BIT_PARTICLE_1_SIXWAY_RAMPMAP,
|
||||
rampProperty.textureValue != null,
|
||||
1);
|
||||
}
|
||||
|
||||
private static bool IsToggleOn(NBShaderRootItem rootItem, string propertyName)
|
||||
{
|
||||
return rootItem.Context.IsToggleOn(propertyName);
|
||||
}
|
||||
|
||||
private static bool IsBlinnOrHalf(NBShaderRootItem rootItem)
|
||||
{
|
||||
return rootItem.Context.FxLightMode == FxLightMode.BlinnPhong ||
|
||||
rootItem.Context.FxLightMode == FxLightMode.HalfLambert;
|
||||
}
|
||||
|
||||
private static GUIContent Content(string key, string fallback, string tip = "")
|
||||
{
|
||||
return NBShaderInspectorLocalization.MakeInspectorContent(key, fallback, tip);
|
||||
}
|
||||
}
|
||||
|
||||
public class FxLightModePopupItem : ShaderGUIPopUpItem
|
||||
{
|
||||
private static readonly string[] LightModeOptions =
|
||||
{
|
||||
"Unlit",
|
||||
"BlinnPhong",
|
||||
"HalfLambert",
|
||||
"PBR",
|
||||
"SixWay"
|
||||
};
|
||||
|
||||
private readonly NBShaderRootItem _nbRootItem;
|
||||
|
||||
public FxLightModePopupItem(NBShaderRootItem rootItem, ShaderGUIItem parentItem) : base(rootItem, parentItem)
|
||||
{
|
||||
_nbRootItem = rootItem;
|
||||
PropertyName = "_FxLightMode";
|
||||
GuiContent = NBShaderInspectorLocalization.MakeContent("inspector.light.mode.label", "Light Mode");
|
||||
PopUpNames = NBShaderInspectorLocalization.GetInspectorOptions("light.mode", LightModeOptions);
|
||||
InitTriggerByChild();
|
||||
}
|
||||
|
||||
public override void OnGUI()
|
||||
{
|
||||
base.OnGUI();
|
||||
}
|
||||
|
||||
public override void OnEndChange()
|
||||
{
|
||||
base.OnEndChange();
|
||||
_nbRootItem.SyncService.ApplyLightMode((FxLightMode)PropertyInfo.Property.floatValue);
|
||||
_nbRootItem.Context.Refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e6914407bf83004f8b2c13ccf777981
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,260 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using NBShader;
|
||||
|
||||
namespace NBShaderEditor
|
||||
{
|
||||
public class MainTexBigBlockItem : BigBlockItem
|
||||
{
|
||||
private readonly NBShaderRootItem _nbRootItem;
|
||||
private readonly TexturePropertyGroupItem _baseMapGroupItem;
|
||||
private readonly TextureRelatedFoldOutItem _baseMapRelatedFoldOutItem;
|
||||
private readonly ColorLineItem _uiColorItem;
|
||||
private readonly TextureScaleOffsetItem _uiMainTexScaleOffsetItem;
|
||||
private readonly ColorChannelSelectItem _alphaChannelItem;
|
||||
private readonly WrapModeItem _baseMapWrapModeItem;
|
||||
private readonly UVModeSelectItem _uvModeItem;
|
||||
private readonly CustomDataSelectItem _offsetXCustomDataItem;
|
||||
private readonly CustomDataSelectItem _offsetYCustomDataItem;
|
||||
private readonly Vector2LineItem _baseMapOffsetSpeedItem;
|
||||
private readonly ShaderGUISliderItem _baseMapRotationItem;
|
||||
private readonly ShaderGUIFloatItem _baseMapRotationSpeedItem;
|
||||
private readonly ShaderGUISliderItem _texDistortionIntensityItem;
|
||||
private readonly PNoiseBlendModeItem _pNoiseBlendModeItem;
|
||||
private readonly HelpBoxItem _graphicMainTexHelpBox;
|
||||
|
||||
public MainTexBigBlockItem(NBShaderRootItem rootItem, ShaderGUIItem parentItem)
|
||||
: base(
|
||||
rootItem,
|
||||
parentItem,
|
||||
"_MainTexBigBlockItemFoldOut",
|
||||
() => NBShaderInspectorLocalization.MakeContent(
|
||||
"inspector.block.maintex.label",
|
||||
"Main Texture",
|
||||
"inspector.block.maintex.tip",
|
||||
"Main texture and base color controls"))
|
||||
{
|
||||
_nbRootItem = rootItem;
|
||||
_baseMapGroupItem = new TexturePropertyGroupItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_BaseMap",
|
||||
"_BaseColor",
|
||||
() => NBShaderInspectorLocalization.MakeContent(
|
||||
"inspector.maintex.basemap.label",
|
||||
"Main Texture"),
|
||||
isVisible: () => rootItem.Context.UseGraphicMainTex == MixedBool.False,
|
||||
tillingContentProvider: TillingContent,
|
||||
offsetContentProvider: OffsetContent);
|
||||
|
||||
_baseMapRelatedFoldOutItem = new TextureRelatedFoldOutItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_BaseMapFoldOut",
|
||||
"_BaseMap",
|
||||
() => NBShaderInspectorLocalization.MakeContent(
|
||||
"inspector.maintex.basemap.related.label",
|
||||
"Main Texture Related"),
|
||||
() => rootItem.Context.UseGraphicMainTex == MixedBool.False);
|
||||
|
||||
_graphicMainTexHelpBox = new HelpBoxItem(
|
||||
rootItem,
|
||||
this,
|
||||
() => NBShaderInspectorLocalization.Get(
|
||||
"inspector.maintex.graphic.message",
|
||||
"Current mode uses Graphic texture. Only color and ST remain editable."),
|
||||
MessageType.Info);
|
||||
|
||||
_uiColorItem = new ColorLineItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_Color",
|
||||
showLabel: false,
|
||||
contentProvider: () => NBShaderInspectorLocalization.MakeContent(
|
||||
"inspector.maintex.uicolor.label",
|
||||
"Graphic Color"),
|
||||
isVisible: () => rootItem.Context.UseGraphicMainTex == MixedBool.True);
|
||||
|
||||
_uiMainTexScaleOffsetItem = new TextureScaleOffsetItem(
|
||||
rootItem,
|
||||
this,
|
||||
"_UI_MainTex_ST",
|
||||
isVectorProperty: true,
|
||||
isVisible: () => rootItem.Context.UseGraphicMainTex == MixedBool.True,
|
||||
tillingContentProvider: TillingContent,
|
||||
offsetContentProvider: OffsetContent);
|
||||
|
||||
_alphaChannelItem = new ColorChannelSelectItem(
|
||||
rootItem,
|
||||
_baseMapRelatedFoldOutItem,
|
||||
NBShaderFlags.FLAG_BIT_COLOR_CHANNEL_POS_0_MAINTEX_ALPHA,
|
||||
3,
|
||||
() => NBShaderInspectorLocalization.MakeContent(
|
||||
"inspector.maintex.alphaChannel.label",
|
||||
"Alpha Channel"));
|
||||
|
||||
_baseMapWrapModeItem = new WrapModeItem(
|
||||
rootItem,
|
||||
_baseMapRelatedFoldOutItem,
|
||||
NBShaderFlags.FLAG_BIT_WRAPMODE_BASEMAP,
|
||||
() => NBShaderInspectorLocalization.MakeContent(
|
||||
"inspector.maintex.wrap.label",
|
||||
"Main Texture Wrap"),
|
||||
2,
|
||||
() => rootItem.Context.UseGraphicMainTex == MixedBool.False);
|
||||
|
||||
_uvModeItem = new UVModeSelectItem(
|
||||
rootItem,
|
||||
_baseMapRelatedFoldOutItem,
|
||||
"_MainTexUVModeFoldOut",
|
||||
NBShaderFlags.FLAG_BIT_UVMODE_POS_0_MAINTEX,
|
||||
0,
|
||||
() => NBShaderInspectorLocalization.MakeContent(
|
||||
"inspector.maintex.uvmode.label",
|
||||
"Main Texture UV Source"),
|
||||
forceEnable: true,
|
||||
isVisible: () => rootItem.Context.MeshSourceMode != MeshSourceMode.UIEffectSprite);
|
||||
|
||||
_offsetXCustomDataItem = new CustomDataSelectItem(
|
||||
rootItem,
|
||||
_baseMapRelatedFoldOutItem,
|
||||
NBShaderFlags.FLAGBIT_POS_0_CUSTOMDATA_MAINTEX_OFFSET_X,
|
||||
0,
|
||||
() => NBShaderInspectorLocalization.MakeContent(
|
||||
"inspector.maintex.customdata.offsetx.label",
|
||||
"Main Texture Offset X Custom Data"),
|
||||
() => rootItem.Context.ParticleMode == MixedBool.True);
|
||||
|
||||
_offsetYCustomDataItem = new CustomDataSelectItem(
|
||||
rootItem,
|
||||
_baseMapRelatedFoldOutItem,
|
||||
NBShaderFlags.FLAGBIT_POS_0_CUSTOMDATA_MAINTEX_OFFSET_Y,
|
||||
0,
|
||||
() => NBShaderInspectorLocalization.MakeContent(
|
||||
"inspector.maintex.customdata.offsety.label",
|
||||
"Main Texture Offset Y Custom Data"),
|
||||
() => rootItem.Context.ParticleMode == MixedBool.True);
|
||||
|
||||
_baseMapOffsetSpeedItem = new Vector2LineItem(
|
||||
rootItem,
|
||||
_baseMapRelatedFoldOutItem,
|
||||
"_BaseMapMaskMapOffset",
|
||||
true,
|
||||
() => NBShaderInspectorLocalization.MakeContent(
|
||||
"inspector.maintex.offsetspeed.label",
|
||||
"Offset Speed"),
|
||||
isVisible: () => rootItem.Context.UseGraphicMainTex == MixedBool.False);
|
||||
|
||||
_baseMapRotationItem = new ShaderGUISliderItem(rootItem, _baseMapRelatedFoldOutItem)
|
||||
{
|
||||
PropertyName = "_BaseMapUVRotation",
|
||||
GuiContent = NBShaderInspectorLocalization.MakeContent(
|
||||
"inspector.maintex.rotation.label",
|
||||
"Rotation"),
|
||||
Min = 0f,
|
||||
Max = 360f
|
||||
};
|
||||
_baseMapRotationItem.InitTriggerByChild();
|
||||
|
||||
_baseMapRotationSpeedItem = new ShaderGUIFloatItem(rootItem, _baseMapRelatedFoldOutItem)
|
||||
{
|
||||
PropertyName = "_BaseMapUVRotationSpeed",
|
||||
GuiContent = NBShaderInspectorLocalization.MakeContent(
|
||||
"inspector.maintex.rotationspeed.label",
|
||||
"Rotation Speed")
|
||||
};
|
||||
_baseMapRotationSpeedItem.InitTriggerByChild();
|
||||
|
||||
_texDistortionIntensityItem = new ShaderGUISliderItem(rootItem, _baseMapRelatedFoldOutItem)
|
||||
{
|
||||
PropertyName = "_TexDistortion_intensity",
|
||||
GuiContent = NBShaderInspectorLocalization.MakeContent(
|
||||
"inspector.maintex.distortionIntensity.label",
|
||||
"Main Texture Distortion"),
|
||||
RangePropertyName = "TexDistortionintensityRangeVec"
|
||||
};
|
||||
_texDistortionIntensityItem.InitTriggerByChild();
|
||||
|
||||
_pNoiseBlendModeItem = new PNoiseBlendModeItem(
|
||||
rootItem,
|
||||
_baseMapRelatedFoldOutItem,
|
||||
NBShaderFlags.FLAG_BIT_PNOISE_BLEND_POS_0_MAINTEX,
|
||||
"_MainTexPNoiseBlendOpacity",
|
||||
() => NBShaderInspectorLocalization.MakeContent(
|
||||
"inspector.maintex.pnoiseBlend.label",
|
||||
"Main Texture Program Noise Blend"),
|
||||
() => rootItem.Context.ProgramNoiseEnabled == MixedBool.True);
|
||||
|
||||
InitTriggerByChild();
|
||||
}
|
||||
|
||||
public override void DrawBlock()
|
||||
{
|
||||
if (_nbRootItem.Context.UseGraphicMainTex == MixedBool.Mixed)
|
||||
{
|
||||
DrawLayoutHelpBox(
|
||||
NBShaderInspectorLocalization.GetInspectorText(
|
||||
"maintex.mixedMeshSource.message",
|
||||
"Mixed mesh-source modes will show the matching texture controls per material state."),
|
||||
MessageType.Info);
|
||||
}
|
||||
|
||||
_baseMapGroupItem.OnGUI();
|
||||
|
||||
if (_nbRootItem.Context.UseGraphicMainTex == MixedBool.True)
|
||||
{
|
||||
_graphicMainTexHelpBox.OnGUI();
|
||||
}
|
||||
|
||||
_uiColorItem.OnGUI();
|
||||
_uiMainTexScaleOffsetItem.OnGUI();
|
||||
if (_nbRootItem.Context.UseGraphicMainTex == MixedBool.False)
|
||||
{
|
||||
_baseMapRelatedFoldOutItem.OnGUI();
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawMainTexRelatedItems();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawMainTexRelatedItems()
|
||||
{
|
||||
_alphaChannelItem.OnGUI();
|
||||
_baseMapWrapModeItem.OnGUI();
|
||||
_uvModeItem.OnGUI();
|
||||
_offsetXCustomDataItem.OnGUI();
|
||||
_offsetYCustomDataItem.OnGUI();
|
||||
_baseMapOffsetSpeedItem.OnGUI();
|
||||
if (_nbRootItem.Context.UseGraphicMainTex == MixedBool.False)
|
||||
{
|
||||
_baseMapRotationItem.OnGUI();
|
||||
_baseMapRotationSpeedItem.OnGUI();
|
||||
}
|
||||
|
||||
if (_nbRootItem.Context.UseGraphicMainTex == MixedBool.False)
|
||||
{
|
||||
bool previousMixedValue = EditorGUI.showMixedValue;
|
||||
EditorGUI.showMixedValue = _nbRootItem.Context.NoiseEnabled == MixedBool.Mixed;
|
||||
using (new InheritedControlDisabledScope(_nbRootItem.Context.NoiseEnabled == MixedBool.False))
|
||||
{
|
||||
_texDistortionIntensityItem.OnGUI();
|
||||
}
|
||||
|
||||
EditorGUI.showMixedValue = previousMixedValue;
|
||||
}
|
||||
|
||||
_pNoiseBlendModeItem.OnGUI();
|
||||
}
|
||||
|
||||
private static GUIContent TillingContent()
|
||||
{
|
||||
return NBShaderInspectorLocalization.MakeInspectorContent("common.tilling", "Tilling");
|
||||
}
|
||||
|
||||
private static GUIContent OffsetContent()
|
||||
{
|
||||
return NBShaderInspectorLocalization.MakeInspectorContent("common.offset", "Offset");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7ebc7f22502386443b06ac9aebc4222b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,377 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
namespace NBShaderEditor
|
||||
{
|
||||
public class ModeBigBlockItem : BigBlockItem
|
||||
{
|
||||
private readonly NBShaderRootItem _nbRootItem;
|
||||
|
||||
public ModeBigBlockItem(NBShaderRootItem rootItem, ShaderGUIItem parentItem)
|
||||
: base(
|
||||
rootItem,
|
||||
parentItem,
|
||||
"_BigBlockModeSettingFoldOut",
|
||||
() => NBShaderInspectorLocalization.MakeContent(
|
||||
"inspector.block.mode.label",
|
||||
"模式设置",
|
||||
"inspector.block.mode.tip",
|
||||
"各种基础模式设置"))
|
||||
{
|
||||
_nbRootItem = rootItem;
|
||||
_meshModePopUp = new MeshModePopUp(rootItem, this);
|
||||
_transparentMode = new TransparentModePopUp(rootItem, this);
|
||||
base.InitTriggerByChild();
|
||||
}
|
||||
|
||||
private MeshModePopUp _meshModePopUp;
|
||||
private TransparentModePopUp _transparentMode;
|
||||
|
||||
public override void DrawBlock()
|
||||
{
|
||||
_meshModePopUp.OnGUI();
|
||||
_transparentMode.OnGUI();
|
||||
}
|
||||
}
|
||||
public enum MeshSourceMode
|
||||
{
|
||||
Particle,
|
||||
Mesh,
|
||||
UIEffectRawImage,
|
||||
UIEffectSprite,
|
||||
UIEffectBaseMap,
|
||||
UIParticle,
|
||||
UnKnowOrMixed = -1
|
||||
}
|
||||
|
||||
public class MeshModePopUp : ShaderGUIPopUpItem,IDisposable
|
||||
{
|
||||
public MeshSourceMode MeshSourceMode;
|
||||
public static Dictionary<ShaderGUIRootItem,MeshModePopUp> MeshSourceModeDic = new Dictionary<ShaderGUIRootItem,MeshModePopUp>();
|
||||
public MeshModePopUp(ShaderGUIRootItem rootItem, ShaderGUIItem parentItem) :
|
||||
base(rootItem, parentItem: parentItem)
|
||||
{
|
||||
PopUpNames = NBShaderInspectorLocalization.GetInspectorOptions("mode.meshSource", MeshSourceOptions);
|
||||
PropertyName = "_MeshSourceMode";
|
||||
GuiContent = NBShaderInspectorLocalization.MakeInspectorContent("mode.meshSource", "Mesh Source Mode", "Matches the current object type.");
|
||||
MeshSourceModeDic[rootItem] = this;
|
||||
InitTriggerByChild();
|
||||
}
|
||||
|
||||
public MixedBool UIEffectEnabled()
|
||||
{
|
||||
if ((int)MeshSourceMode >= 2)
|
||||
{
|
||||
return MixedBool.True;
|
||||
}
|
||||
else if (MeshSourceMode == MeshSourceMode.UnKnowOrMixed)
|
||||
{
|
||||
return MixedBool.Mixed;
|
||||
}
|
||||
else
|
||||
{
|
||||
return MixedBool.False;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnGUI()
|
||||
{
|
||||
base.OnGUI();
|
||||
if (PropertyInfo.Property.hasMixedValue)
|
||||
{
|
||||
MeshSourceMode = MeshSourceMode.UnKnowOrMixed;
|
||||
}
|
||||
else
|
||||
{
|
||||
MeshSourceMode = (MeshSourceMode)PropertyInfo.Property.floatValue;
|
||||
}
|
||||
|
||||
DrawParticleSystemMismatchWarning();
|
||||
}
|
||||
|
||||
public override void OnEndChange()
|
||||
{
|
||||
base.OnEndChange();
|
||||
if (RootItem is NBShaderRootItem nbRootItem)
|
||||
{
|
||||
nbRootItem.Context.Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
MeshSourceModeDic.Remove(RootItem);
|
||||
}
|
||||
|
||||
private void DrawParticleSystemMismatchWarning()
|
||||
{
|
||||
if (!(RootItem is NBShaderRootItem nbRootItem) ||
|
||||
nbRootItem.Mats == null ||
|
||||
nbRootItem.Mats.Count == 0 ||
|
||||
nbRootItem.Context.ParticleMode == MixedBool.True ||
|
||||
!nbRootItem.IsUsedByParticleSystem)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DrawLayoutHelpBox(
|
||||
NBShaderInspectorLocalization.GetInspectorText(
|
||||
"mode.meshSource.particleModeMismatch",
|
||||
"检测到材质用在粒子系统上,和设置不匹配"),
|
||||
MessageType.Error);
|
||||
}
|
||||
|
||||
private static readonly string[] MeshSourceOptions =
|
||||
{
|
||||
"粒子系统",
|
||||
"模型(非粒子发射)",
|
||||
"2D RawImage",
|
||||
"2D 精灵",
|
||||
"2D 材质贴图",
|
||||
"2D UIParticle"
|
||||
};
|
||||
}
|
||||
|
||||
public enum TransparentMode
|
||||
{
|
||||
Opaque = 0,
|
||||
Transparent = 1,
|
||||
CutOff = 2,
|
||||
UnKnowOrMixed = -1
|
||||
}
|
||||
|
||||
public class TransparentModePopUp : ShaderGUIPopUpItem,IDisposable
|
||||
{
|
||||
|
||||
public TransparentMode TransparentMode;
|
||||
public static Dictionary<ShaderGUIRootItem,TransparentModePopUp> TransparentModeDic =
|
||||
new Dictionary<ShaderGUIRootItem, TransparentModePopUp>();
|
||||
public TransparentModePopUp(ShaderGUIRootItem rootItem, ShaderGUIItem parentItem) :
|
||||
base(rootItem, parentItem: parentItem)
|
||||
{
|
||||
PopUpNames = NBShaderInspectorLocalization.GetInspectorOptions("mode.transparent", TransparentOptions);
|
||||
PropertyName = "_TransparentMode";
|
||||
GuiContent = NBShaderInspectorLocalization.MakeInspectorContent("mode.transparent", "Transparent Mode", "Controls opaque, transparent, and cutoff rendering.");
|
||||
TransparentModeDic[RootItem] = this;
|
||||
_cutOffSlider = new ShaderGUISliderItem(RootItem, this)
|
||||
{
|
||||
PropertyName = "_Cutoff",
|
||||
GuiContent = NBShaderInspectorLocalization.MakeInspectorContent("mode.cutoff", "Cutoff", "0 keeps everything, 1 clips everything.")
|
||||
};
|
||||
_cutOffSlider.InitTriggerByChild();
|
||||
_blendPopUp = new BlendPopUp(RootItem, this);
|
||||
PropertyInfo = RootItem.PropertyInfoDic[PropertyName];
|
||||
TransparentMode = (TransparentMode)PropertyInfo.Property.floatValue;
|
||||
InitTriggerByChild();
|
||||
}
|
||||
|
||||
|
||||
public override void OnGUI()
|
||||
{
|
||||
base.OnGUI();
|
||||
if (PropertyInfo.Property.hasMixedValue)
|
||||
{
|
||||
TransparentMode = TransparentMode.UnKnowOrMixed;
|
||||
}
|
||||
else
|
||||
{
|
||||
TransparentMode = (TransparentMode)PropertyInfo.Property.floatValue;
|
||||
}
|
||||
}
|
||||
|
||||
private ShaderGUISliderItem _cutOffSlider;
|
||||
private BlendPopUp _blendPopUp;
|
||||
public override void DrawBlock()
|
||||
{
|
||||
if (TransparentMode== TransparentMode.CutOff)
|
||||
{
|
||||
_cutOffSlider.OnGUI();
|
||||
}
|
||||
|
||||
if (TransparentMode == TransparentMode.Transparent)
|
||||
{
|
||||
_blendPopUp.OnGUI();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void OnEndChange()
|
||||
{
|
||||
TransparentMode = (TransparentMode)PropertyInfo.Property.floatValue;
|
||||
switch (TransparentMode)
|
||||
{
|
||||
case TransparentMode.Opaque:
|
||||
if (RootItem is NBShaderRootItem opaqueRootItem)
|
||||
{
|
||||
opaqueRootItem.SyncService.ApplyTransparentMode(TransparentMode);
|
||||
}
|
||||
_blendPopUp.PropertyInfo.Property.floatValue = (float)BlendMode.Opaque;
|
||||
_blendPopUp.OnEndChange();
|
||||
break;
|
||||
case TransparentMode.Transparent:
|
||||
if (RootItem is NBShaderRootItem transparentRootItem)
|
||||
{
|
||||
transparentRootItem.SyncService.ApplyTransparentMode(TransparentMode);
|
||||
}
|
||||
|
||||
if (_blendPopUp.BlendMode == BlendMode.Opaque)
|
||||
{
|
||||
_blendPopUp.PropertyInfo.Property.floatValue = (float)BlendMode.Alpha;//如果设置错误则强制设置。
|
||||
_blendPopUp.OnEndChange();
|
||||
}
|
||||
|
||||
break;
|
||||
case TransparentMode.CutOff:
|
||||
if (RootItem is NBShaderRootItem cutOffRootItem)
|
||||
{
|
||||
cutOffRootItem.SyncService.ApplyTransparentMode(TransparentMode);
|
||||
}
|
||||
_blendPopUp.PropertyInfo.Property.floatValue = (float)BlendMode.Opaque;
|
||||
_blendPopUp.OnEndChange();
|
||||
break;
|
||||
}
|
||||
|
||||
base.OnEndChange();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
TransparentModeDic.Remove(RootItem);//回收时清掉相关引用。?会不会有时序问题
|
||||
}
|
||||
|
||||
private static readonly string[] TransparentOptions =
|
||||
{
|
||||
"不透明Opaque",
|
||||
"半透明Transparent",
|
||||
"不透明裁剪CutOff"
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
public enum BlendMode
|
||||
{
|
||||
Alpha, // Old school alpha-blending mode, fresnel does not affect amount of transparency
|
||||
Premultiply, // Physically plausible transparency mode, implemented as alpha pre-multiply
|
||||
Additive,
|
||||
Multiply,
|
||||
Opaque,
|
||||
UnKnowOrMixed = -1
|
||||
}
|
||||
|
||||
public class BlendPopUp : ShaderGUIPopUpItem,IDisposable
|
||||
{
|
||||
public BlendMode BlendMode;
|
||||
public static Dictionary<ShaderGUIRootItem,BlendPopUp> BlendModeDic = new Dictionary<ShaderGUIRootItem,BlendPopUp>();
|
||||
public BlendPopUp(ShaderGUIRootItem rootItem, ShaderGUIItem parentItem) : base(rootItem, parentItem)
|
||||
{
|
||||
PropertyName = "_Blend";
|
||||
PopUpNames = NBShaderInspectorLocalization.GetInspectorOptions("mode.blend", BlendOptions);
|
||||
GuiContent = NBShaderInspectorLocalization.MakeInspectorContent("mode.blend", "Blend Mode");
|
||||
BlendModeDic[rootItem] = this;
|
||||
_addToPreMultiplySlider = new AddToPreMultiplySlider(rootItem, this);
|
||||
InitTriggerByChild();
|
||||
}
|
||||
|
||||
public override void OnGUI()
|
||||
{
|
||||
base.OnGUI();
|
||||
if (PropertyInfo.Property.hasMixedValue)
|
||||
{
|
||||
BlendMode = BlendMode.UnKnowOrMixed;
|
||||
}
|
||||
else
|
||||
{
|
||||
BlendMode = (BlendMode)PropertyInfo.Property.floatValue;
|
||||
}
|
||||
}
|
||||
|
||||
private AddToPreMultiplySlider _addToPreMultiplySlider;
|
||||
public override void DrawBlock()
|
||||
{
|
||||
if (BlendMode == BlendMode.Additive || BlendMode == BlendMode.Premultiply)
|
||||
{
|
||||
_addToPreMultiplySlider.OnGUI();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnEndChange()
|
||||
{
|
||||
BlendMode = (BlendMode)PropertyInfo.Property.floatValue;
|
||||
if (BlendMode == BlendMode.Additive)
|
||||
{
|
||||
_addToPreMultiplySlider.PropertyInfo.Property.floatValue = 0;
|
||||
_addToPreMultiplySlider.OnEndChange();
|
||||
}
|
||||
|
||||
if (BlendMode == BlendMode.Premultiply)
|
||||
{
|
||||
_addToPreMultiplySlider.PropertyInfo.Property.floatValue = 1;
|
||||
_addToPreMultiplySlider.OnEndChange();
|
||||
}
|
||||
|
||||
if (RootItem is NBShaderRootItem nbRootItem)
|
||||
{
|
||||
nbRootItem.SyncService.ApplyBlendMode(BlendMode);
|
||||
}
|
||||
|
||||
base.OnEndChange();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
BlendModeDic.Remove(RootItem);
|
||||
}
|
||||
|
||||
private static readonly string[] BlendOptions =
|
||||
{
|
||||
"透明度混合AlphaBlend",
|
||||
"预乘PreMultiply",
|
||||
"叠加Additive",
|
||||
"正片叠底Multiply"
|
||||
};
|
||||
}
|
||||
|
||||
public class AddToPreMultiplySlider : ShaderGUISliderItem
|
||||
{
|
||||
public AddToPreMultiplySlider(ShaderGUIRootItem rootItem, ShaderGUIItem parentItem) : base(rootItem, parentItem)
|
||||
{
|
||||
PropertyName = "_AdditiveToPreMultiplyAlphaLerp";
|
||||
GuiContent = NBShaderInspectorLocalization.MakeInspectorContent("mode.additiveToPremultiply", "Additive To Premultiply", "0 is additive, 1 is premultiply.");
|
||||
base.InitTriggerByChild();
|
||||
}
|
||||
|
||||
public override void OnGUI()
|
||||
{
|
||||
base.OnGUI();
|
||||
}
|
||||
|
||||
public override void CheckIsPropertyModified(bool isCallByChild = false)
|
||||
{
|
||||
float defaultValue = 0;
|
||||
BlendPopUp blendPopUp = BlendPopUp.BlendModeDic[RootItem];
|
||||
if (blendPopUp.BlendMode == BlendMode.Premultiply)
|
||||
{
|
||||
defaultValue = 1;
|
||||
}
|
||||
|
||||
HasModified = !Mathf.Approximately(defaultValue,PropertyInfo.Property.floatValue);
|
||||
ParentItem?.CheckIsPropertyModified(true);
|
||||
|
||||
}
|
||||
|
||||
public override void ExecuteReset(bool isCallByParent = false)
|
||||
{
|
||||
float defaultValue = 0;
|
||||
BlendPopUp blendPopUp = BlendPopUp.BlendModeDic[RootItem];
|
||||
if (blendPopUp.BlendMode == BlendMode.Premultiply)
|
||||
{
|
||||
defaultValue = 1;
|
||||
}
|
||||
|
||||
PropertyInfo.Property.floatValue = defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ed09665b113c407dbfb8c818b74f48aa
|
||||
timeCreated: 1758445745
|
||||
@@ -0,0 +1,220 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NBShader;
|
||||
using NBShaders2.Editor.FeatureLevel;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBShaderEditor
|
||||
{
|
||||
public class NBShaderGUIContext
|
||||
{
|
||||
private const string FeatureTierPropertyName = "_NBShaderFeatureTier";
|
||||
|
||||
private readonly NBShaderRootItem _rootItem;
|
||||
private HashSet<string> _currentTierAllowedKeywords;
|
||||
|
||||
public NBShaderGUIContext(NBShaderRootItem rootItem)
|
||||
{
|
||||
_rootItem = rootItem;
|
||||
}
|
||||
|
||||
public MeshSourceMode MeshSourceMode { get; private set; } = MeshSourceMode.UnKnowOrMixed;
|
||||
public TransparentMode TransparentMode { get; private set; } = TransparentMode.UnKnowOrMixed;
|
||||
public MixedBool UIEffectEnabled { get; private set; } = MixedBool.Mixed;
|
||||
public MixedBool UseGraphicMainTex { get; private set; } = MixedBool.Mixed;
|
||||
public MixedBool ParticleMode { get; private set; } = MixedBool.Mixed;
|
||||
public MixedBool NoiseEnabled { get; private set; } = MixedBool.Mixed;
|
||||
public MixedBool ProgramNoiseEnabled { get; private set; } = MixedBool.Mixed;
|
||||
public MixedBool VatEnabled { get; private set; } = MixedBool.Mixed;
|
||||
public MixedBool FlipbookEnabled { get; private set; } = MixedBool.Mixed;
|
||||
public FxLightMode FxLightMode { get; private set; } = FxLightMode.UnKnownOrMixedValue;
|
||||
public NBShaderFeatureTier CurrentTier { get; private set; } = NBShaderFeatureTier.Ultra;
|
||||
public bool CurrentTierMixed { get; private set; }
|
||||
|
||||
public bool IsKeywordAllowed(string keyword)
|
||||
{
|
||||
if (!NBShaderFeatureCatalog.IsManagedKeyword(keyword))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (CurrentTierMixed)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return _currentTierAllowedKeywords != null && _currentTierAllowedKeywords.Contains(keyword);
|
||||
}
|
||||
|
||||
public bool AreKeywordsAllowed(params string[] keywords)
|
||||
{
|
||||
if (keywords == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < keywords.Length; i++)
|
||||
{
|
||||
if (!IsKeywordAllowed(keywords[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsAnyKeywordAllowed(params string[] keywords)
|
||||
{
|
||||
if (keywords == null || keywords.Length == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < keywords.Length; i++)
|
||||
{
|
||||
if (IsKeywordAllowed(keywords[i]))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsAnyKeywordAllowed(string keyword0, string keyword1)
|
||||
{
|
||||
return IsKeywordAllowed(keyword0) || IsKeywordAllowed(keyword1);
|
||||
}
|
||||
|
||||
public bool IsAnyKeywordAllowed(
|
||||
string keyword0,
|
||||
string keyword1,
|
||||
string keyword2,
|
||||
string keyword3,
|
||||
string keyword4)
|
||||
{
|
||||
return IsKeywordAllowed(keyword0) ||
|
||||
IsKeywordAllowed(keyword1) ||
|
||||
IsKeywordAllowed(keyword2) ||
|
||||
IsKeywordAllowed(keyword3) ||
|
||||
IsKeywordAllowed(keyword4);
|
||||
}
|
||||
|
||||
public static bool IsCatalogKeyword(string keyword)
|
||||
{
|
||||
return NBShaderFeatureCatalog.IsManagedKeyword(keyword);
|
||||
}
|
||||
|
||||
public bool HasProperty(string propertyName)
|
||||
{
|
||||
return _rootItem.PropertyInfoDic.ContainsKey(propertyName);
|
||||
}
|
||||
|
||||
public MaterialProperty GetProperty(string propertyName)
|
||||
{
|
||||
return _rootItem.PropertyInfoDic[propertyName].Property;
|
||||
}
|
||||
|
||||
public void Refresh()
|
||||
{
|
||||
RefreshFeatureTier();
|
||||
|
||||
if (HasProperty("_MeshSourceMode"))
|
||||
{
|
||||
MaterialProperty meshSourceModeProperty = GetProperty("_MeshSourceMode");
|
||||
MeshSourceMode = meshSourceModeProperty.hasMixedValue
|
||||
? MeshSourceMode.UnKnowOrMixed
|
||||
: (MeshSourceMode)meshSourceModeProperty.floatValue;
|
||||
}
|
||||
|
||||
if (HasProperty("_TransparentMode"))
|
||||
{
|
||||
MaterialProperty transparentModeProperty = GetProperty("_TransparentMode");
|
||||
TransparentMode = transparentModeProperty.hasMixedValue
|
||||
? TransparentMode.UnKnowOrMixed
|
||||
: (TransparentMode)transparentModeProperty.floatValue;
|
||||
}
|
||||
|
||||
if (MeshSourceMode == MeshSourceMode.UnKnowOrMixed)
|
||||
{
|
||||
UIEffectEnabled = MixedBool.Mixed;
|
||||
UseGraphicMainTex = MixedBool.Mixed;
|
||||
ParticleMode = MixedBool.Mixed;
|
||||
}
|
||||
else
|
||||
{
|
||||
UIEffectEnabled = (int)MeshSourceMode >= 2 ? MixedBool.True : MixedBool.False;
|
||||
UseGraphicMainTex = MeshSourceMode == MeshSourceMode.UIEffectRawImage || MeshSourceMode == MeshSourceMode.UIEffectSprite
|
||||
? MixedBool.True
|
||||
: MixedBool.False;
|
||||
ParticleMode = MeshSourceMode == MeshSourceMode.Particle || MeshSourceMode == MeshSourceMode.UIParticle
|
||||
? MixedBool.True
|
||||
: MixedBool.False;
|
||||
}
|
||||
|
||||
if (HasProperty("_FxLightMode"))
|
||||
{
|
||||
MaterialProperty lightModeProperty = GetProperty("_FxLightMode");
|
||||
FxLightMode = lightModeProperty.hasMixedValue
|
||||
? FxLightMode.UnKnownOrMixedValue
|
||||
: (FxLightMode)lightModeProperty.floatValue;
|
||||
}
|
||||
|
||||
NoiseEnabled = IsKeywordAllowed("_NOISEMAP") ? GetToggleState("_noisemapEnabled") : MixedBool.False;
|
||||
ProgramNoiseEnabled = IsKeywordAllowed("_PROGRAM_NOISE") ? GetToggleState("_ProgramNoise_Toggle") : MixedBool.False;
|
||||
VatEnabled = IsKeywordAllowed("_VAT") ? GetToggleState("_VAT_Toggle") : MixedBool.False;
|
||||
FlipbookEnabled = IsKeywordAllowed("_FLIPBOOKBLENDING_ON") ? GetToggleState("_FlipbookBlending") : MixedBool.False;
|
||||
}
|
||||
|
||||
private void RefreshFeatureTier()
|
||||
{
|
||||
if (!HasProperty(FeatureTierPropertyName))
|
||||
{
|
||||
CurrentTier = NBShaderFeatureTier.Ultra;
|
||||
CurrentTierMixed = false;
|
||||
_currentTierAllowedKeywords =
|
||||
NBShaderFeatureLevelProjectSettings.instance.GetAllowedKeywordSetForReadOnlyUse(CurrentTier);
|
||||
return;
|
||||
}
|
||||
|
||||
MaterialProperty tierProperty = GetProperty(FeatureTierPropertyName);
|
||||
CurrentTierMixed = tierProperty.hasMixedValue;
|
||||
CurrentTier = CurrentTierMixed
|
||||
? NBShaderFeatureTier.Ultra
|
||||
: ToFeatureTier(Mathf.RoundToInt(tierProperty.floatValue));
|
||||
_currentTierAllowedKeywords = CurrentTierMixed
|
||||
? null
|
||||
: NBShaderFeatureLevelProjectSettings.instance.GetAllowedKeywordSetForReadOnlyUse(CurrentTier);
|
||||
}
|
||||
|
||||
private static NBShaderFeatureTier ToFeatureTier(int value)
|
||||
{
|
||||
return Enum.IsDefined(typeof(NBShaderFeatureTier), value) && value >= 0
|
||||
? (NBShaderFeatureTier)value
|
||||
: NBShaderFeatureTier.Ultra;
|
||||
}
|
||||
|
||||
public MixedBool GetToggleState(string propertyName)
|
||||
{
|
||||
if (!HasProperty(propertyName))
|
||||
{
|
||||
return MixedBool.False;
|
||||
}
|
||||
|
||||
MaterialProperty property = GetProperty(propertyName);
|
||||
if (property.hasMixedValue)
|
||||
{
|
||||
return MixedBool.Mixed;
|
||||
}
|
||||
|
||||
return property.floatValue > 0.5f ? MixedBool.True : MixedBool.False;
|
||||
}
|
||||
|
||||
public bool IsToggleOn(string propertyName)
|
||||
{
|
||||
return GetToggleState(propertyName) == MixedBool.True;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fa789821f4c5470479fc50aac3322924
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,715 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using NBShader;
|
||||
using NBShaders2.Editor.FeatureLevel;
|
||||
|
||||
namespace NBShaderEditor
|
||||
{
|
||||
public class NBShaderGUIToolBar
|
||||
{
|
||||
private const float ButtonWidth = 30f;
|
||||
private const float TierButtonWidth = 105f;
|
||||
private const string FeatureTierPropertyName = "_NBShaderFeatureTier";
|
||||
private const string HelpUrl = "https://owejt9diz2c.feishu.cn/wiki/BHz8wHHSjiYJagk7WrmcAcconlb?from=from_copylink";
|
||||
private static readonly string[] SettingsIconNames =
|
||||
{
|
||||
"SettingsIcon",
|
||||
"d_SettingsIcon",
|
||||
"Settings",
|
||||
"d_Settings"
|
||||
};
|
||||
|
||||
private readonly NBShaderRootItem _rootItem;
|
||||
private readonly GUIContent _tierContent = new GUIContent();
|
||||
private readonly string _tierMixedLabel;
|
||||
private readonly string _tierFormat;
|
||||
private readonly string _tierTooltip;
|
||||
private NBShaderFeatureTier _tierContentTier;
|
||||
private bool _tierContentMixed;
|
||||
private bool _tierContentInitialized;
|
||||
private static readonly Dictionary<string, GUIContent> IconContentCache =
|
||||
new Dictionary<string, GUIContent>(StringComparer.Ordinal);
|
||||
private static readonly Dictionary<string, GUIContent> TextContentCache =
|
||||
new Dictionary<string, GUIContent>(StringComparer.Ordinal);
|
||||
private static GUIContent s_SettingsContent;
|
||||
private static string s_ToolbarContentLanguage;
|
||||
|
||||
private static Material copiedMaterialSnapshot;
|
||||
private static Shader copiedShader;
|
||||
|
||||
public NBShaderGUIToolBar(NBShaderRootItem rootItem)
|
||||
{
|
||||
_rootItem = rootItem;
|
||||
_tierMixedLabel = Label("tierMixed", "Tier: Mixed");
|
||||
_tierFormat = Label("tierFormat", "Tier: {0}");
|
||||
_tierTooltip = Tip("tier", "NBShader feature tier");
|
||||
}
|
||||
|
||||
public void DrawToolbar()
|
||||
{
|
||||
Rect toolbarRect = ShaderGUIItem.ApplyGlobalRectCompensation(
|
||||
_rootItem.GetControlRect(EditorGUIUtility.singleLineHeight));
|
||||
GUI.Box(toolbarRect, GUIContent.none, EditorStyles.toolbar);
|
||||
|
||||
Material material = MainMaterial;
|
||||
bool hasMaterial = material != null;
|
||||
float buttonX = toolbarRect.x;
|
||||
|
||||
using (new EditorGUI.DisabledScope(!hasMaterial))
|
||||
{
|
||||
if (ToolbarButton(toolbarRect, ref buttonX, IconContent("Material On Icon", "ping", "跳到当前材质")))
|
||||
{
|
||||
EditorGUIUtility.PingObject(material);
|
||||
}
|
||||
|
||||
if (ToolbarButton(toolbarRect, ref buttonX, IconContent("TreeEditor.Trash", "cleanUnusedTextures", "清除没有使用的贴图")))
|
||||
{
|
||||
CleanUnusedTextures();
|
||||
}
|
||||
|
||||
if (ToolbarButton(toolbarRect, ref buttonX, TextContent("copy", "C", "复制材质属性")))
|
||||
{
|
||||
CopyMaterial(material);
|
||||
}
|
||||
}
|
||||
|
||||
using (new EditorGUI.DisabledScope(!hasMaterial || !HasCopiedMaterial()))
|
||||
{
|
||||
if (ToolbarButton(toolbarRect, ref buttonX, TextContent("paste", "V", "粘贴材质属性")))
|
||||
{
|
||||
PasteMaterial();
|
||||
}
|
||||
}
|
||||
|
||||
using (new EditorGUI.DisabledScope(!hasMaterial))
|
||||
{
|
||||
if (ToolbarButton(toolbarRect, ref buttonX, TextContent("specialReset", "R", "特殊重置功能")))
|
||||
{
|
||||
ShowResetPopupMenu();
|
||||
}
|
||||
|
||||
if (ToolbarButton(toolbarRect, ref buttonX, IconContent("d_UnityEditor.HierarchyWindow", "collapseAll", "折叠所有控件")))
|
||||
{
|
||||
CollapseAll();
|
||||
}
|
||||
}
|
||||
|
||||
float rightX = toolbarRect.xMax;
|
||||
|
||||
rightX -= ButtonWidth;
|
||||
Rect helpRect = MakeToolbarButtonRect(toolbarRect, rightX);
|
||||
if (GUI.Button(helpRect, IconContent("d__Help@2x", "help", "说明文档"), EditorStyles.toolbarButton))
|
||||
{
|
||||
Application.OpenURL(HelpUrl);
|
||||
}
|
||||
|
||||
rightX -= ButtonWidth;
|
||||
Rect settingsRect = MakeToolbarButtonRect(toolbarRect, rightX);
|
||||
if (GUI.Button(settingsRect, SettingsContent(), EditorStyles.toolbarButton))
|
||||
{
|
||||
OpenProjectSettings();
|
||||
}
|
||||
|
||||
rightX -= TierButtonWidth;
|
||||
Rect tierRect = MakeToolbarButtonRect(toolbarRect, rightX, TierButtonWidth);
|
||||
using (new EditorGUI.DisabledScope(!hasMaterial))
|
||||
{
|
||||
if (GUI.Button(tierRect, TierContent(), EditorStyles.toolbarButton))
|
||||
{
|
||||
ShowTierPopupMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ToolbarButton(Rect toolbarRect, ref float buttonX, GUIContent content)
|
||||
{
|
||||
return ToolbarButton(toolbarRect, ref buttonX, content, ButtonWidth);
|
||||
}
|
||||
|
||||
private static bool ToolbarButton(Rect toolbarRect, ref float buttonX, GUIContent content, float width)
|
||||
{
|
||||
Rect buttonRect = MakeToolbarButtonRect(toolbarRect, buttonX, width);
|
||||
buttonX += width;
|
||||
return GUI.Button(buttonRect, content, EditorStyles.toolbarButton);
|
||||
}
|
||||
|
||||
private static Rect MakeToolbarButtonRect(Rect toolbarRect, float x)
|
||||
{
|
||||
return MakeToolbarButtonRect(toolbarRect, x, ButtonWidth);
|
||||
}
|
||||
|
||||
private static Rect MakeToolbarButtonRect(Rect toolbarRect, float x, float width)
|
||||
{
|
||||
return new Rect(x, toolbarRect.y, width, toolbarRect.height);
|
||||
}
|
||||
|
||||
private static GUIContent SettingsContent()
|
||||
{
|
||||
EnsureToolbarContentLanguage();
|
||||
if (s_SettingsContent != null)
|
||||
{
|
||||
return s_SettingsContent;
|
||||
}
|
||||
|
||||
Texture image = null;
|
||||
for (int i = 0; i < SettingsIconNames.Length; i++)
|
||||
{
|
||||
GUIContent icon = EditorGUIUtility.IconContent(SettingsIconNames[i]);
|
||||
if (icon != null && icon.image != null)
|
||||
{
|
||||
image = icon.image;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
s_SettingsContent = new GUIContent(image, Tip("settings", "NBShader Project Settings"));
|
||||
return s_SettingsContent;
|
||||
}
|
||||
|
||||
private static void OpenProjectSettings()
|
||||
{
|
||||
SettingsService.OpenProjectSettings(NBShaderFeatureLevelSettingsProvider.SettingsPath);
|
||||
}
|
||||
|
||||
private static bool HasCopiedMaterial()
|
||||
{
|
||||
return copiedMaterialSnapshot != null;
|
||||
}
|
||||
|
||||
private static void CopyMaterial(Material material)
|
||||
{
|
||||
if (copiedMaterialSnapshot != null)
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(copiedMaterialSnapshot);
|
||||
copiedMaterialSnapshot = null;
|
||||
copiedShader = null;
|
||||
}
|
||||
|
||||
if (material == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
copiedMaterialSnapshot = new Material(material)
|
||||
{
|
||||
hideFlags = HideFlags.HideAndDontSave
|
||||
};
|
||||
copiedShader = copiedMaterialSnapshot.shader;
|
||||
}
|
||||
|
||||
private GUIContent TierContent()
|
||||
{
|
||||
bool mixed = _rootItem.Context != null && _rootItem.Context.CurrentTierMixed;
|
||||
NBShaderFeatureTier tier = CurrentTier;
|
||||
if (!_tierContentInitialized || _tierContentMixed != mixed || _tierContentTier != tier)
|
||||
{
|
||||
_tierContent.text = mixed
|
||||
? _tierMixedLabel
|
||||
: string.Format(_tierFormat, GetTierLabel(tier));
|
||||
_tierContent.tooltip = _tierTooltip;
|
||||
_tierContentTier = tier;
|
||||
_tierContentMixed = mixed;
|
||||
_tierContentInitialized = true;
|
||||
}
|
||||
|
||||
return _tierContent;
|
||||
}
|
||||
|
||||
private NBShaderFeatureTier CurrentTier
|
||||
{
|
||||
get
|
||||
{
|
||||
return _rootItem.Context != null ? _rootItem.Context.CurrentTier : NBShaderFeatureTier.Ultra;
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowTierPopupMenu()
|
||||
{
|
||||
GenericMenu menu = new GenericMenu();
|
||||
AddTierMenuItem(menu, NBShaderFeatureTier.Low);
|
||||
AddTierMenuItem(menu, NBShaderFeatureTier.Medium);
|
||||
AddTierMenuItem(menu, NBShaderFeatureTier.High);
|
||||
AddTierMenuItem(menu, NBShaderFeatureTier.Ultra);
|
||||
menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero));
|
||||
}
|
||||
|
||||
private void AddTierMenuItem(GenericMenu menu, NBShaderFeatureTier tier)
|
||||
{
|
||||
bool isChecked = (_rootItem.Context == null || !_rootItem.Context.CurrentTierMixed) && CurrentTier == tier;
|
||||
menu.AddItem(new GUIContent(GetTierLabel(tier)), isChecked, () => SetFeatureTier(tier));
|
||||
}
|
||||
|
||||
private static string GetTierLabel(NBShaderFeatureTier tier)
|
||||
{
|
||||
switch (tier)
|
||||
{
|
||||
case NBShaderFeatureTier.Low:
|
||||
return Label("tierLow", "Low");
|
||||
case NBShaderFeatureTier.Medium:
|
||||
return Label("tierMedium", "Medium");
|
||||
case NBShaderFeatureTier.High:
|
||||
return Label("tierHigh", "High");
|
||||
case NBShaderFeatureTier.Ultra:
|
||||
return Label("tierUltra", "Ultra");
|
||||
default:
|
||||
return tier.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetFeatureTier(NBShaderFeatureTier tier)
|
||||
{
|
||||
RecordAllMaterials(UndoText("setTier", "Set NBShader Feature Tier"));
|
||||
|
||||
if (_rootItem.PropertyInfoDic.TryGetValue(FeatureTierPropertyName, out ShaderPropertyInfo info))
|
||||
{
|
||||
info.Property.floatValue = (float)tier;
|
||||
}
|
||||
|
||||
foreach (Material mat in Materials)
|
||||
{
|
||||
if (mat.HasProperty(FeatureTierPropertyName))
|
||||
{
|
||||
mat.SetFloat(FeatureTierPropertyName, (float)tier);
|
||||
}
|
||||
|
||||
NBShaderFeatureLevelMaterialApplier.Apply(mat, tier, false, true);
|
||||
}
|
||||
|
||||
FinishFeatureTierMutation();
|
||||
}
|
||||
|
||||
private void ShowResetPopupMenu()
|
||||
{
|
||||
GenericMenu menu = new GenericMenu();
|
||||
menu.AddItem(new GUIContent(Label("resetAll", "重置所有")), false, ResetAll);
|
||||
menu.AddItem(new GUIContent(Label("resetDisabledFeatureChildren", "重置关闭功能子属性")), false, ResetDisabledFeatureChildren);
|
||||
menu.AddSeparator(string.Empty);
|
||||
menu.AddItem(new GUIContent(Label("resetSpecialUV", "重置特殊UV通道")), false, ResetSpecialUVChannel);
|
||||
menu.AddItem(new GUIContent(Label("resetTwirl", "重置旋转扭曲")), false, ResetTwirl);
|
||||
menu.AddItem(new GUIContent(Label("resetPolar", "重置极坐标")), false, ResetPolar);
|
||||
menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero));
|
||||
}
|
||||
|
||||
private void CleanUnusedTextures()
|
||||
{
|
||||
RecordAllMaterials(UndoText("cleanUnusedTextures", "清除没有使用的贴图"));
|
||||
foreach (Material mat in Materials)
|
||||
{
|
||||
CleanUnusedTextureProperties(mat);
|
||||
}
|
||||
|
||||
_rootItem.RequestClearUnusedTextureReferences();
|
||||
MarkAllMaterialsDirty();
|
||||
}
|
||||
|
||||
private void PasteMaterial()
|
||||
{
|
||||
Material material = MainMaterial;
|
||||
if (material == null || !HasCopiedMaterial())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Undo.RecordObject(material, UndoText("paste", "粘贴材质属性"));
|
||||
if (copiedShader != null)
|
||||
{
|
||||
material.shader = copiedShader;
|
||||
}
|
||||
|
||||
material.CopyPropertiesFromMaterial(copiedMaterialSnapshot);
|
||||
|
||||
EditorUtility.SetDirty(material);
|
||||
_rootItem.IsInit = true;
|
||||
_rootItem.Context?.Refresh();
|
||||
_rootItem.SyncService?.SyncMaterialState();
|
||||
_rootItem.Context?.Refresh();
|
||||
UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
|
||||
GUIUtility.ExitGUI();
|
||||
}
|
||||
|
||||
private void ResetSpecialUVChannel()
|
||||
{
|
||||
RecordAllMaterials(UndoText("resetSpecialUV", "重置特殊UV通道"));
|
||||
ResetSpecialUVChannelValues();
|
||||
FinishMaterialMutation();
|
||||
}
|
||||
|
||||
private void ResetTwirl()
|
||||
{
|
||||
RecordAllMaterials(UndoText("resetTwirl", "重置旋转扭曲"));
|
||||
ResetTwirlValues();
|
||||
FinishMaterialMutation();
|
||||
}
|
||||
|
||||
private void ResetPolar()
|
||||
{
|
||||
RecordAllMaterials(UndoText("resetPolar", "重置极坐标"));
|
||||
ResetPolarValues();
|
||||
FinishMaterialMutation();
|
||||
}
|
||||
|
||||
private void ResetAll()
|
||||
{
|
||||
RecordAllMaterials(UndoText("resetAll", "重置所有特殊功能"));
|
||||
_rootItem.ExecuteResetAllItems();
|
||||
ResetSpecialUVChannelValues();
|
||||
ResetTwirlValues();
|
||||
ResetPolarValues();
|
||||
FinishMaterialMutation();
|
||||
}
|
||||
|
||||
private void ResetDisabledFeatureChildren()
|
||||
{
|
||||
RecordAllMaterials(UndoText("resetDisabledFeatureChildren", "重置关闭功能子属性"));
|
||||
foreach (ShaderGUIItem item in _rootItem.GetToolbarResetRootItems())
|
||||
{
|
||||
ResetDisabledFeatureChildren(item);
|
||||
}
|
||||
|
||||
FinishMaterialMutation();
|
||||
}
|
||||
|
||||
private static void ResetDisabledFeatureChildren(ShaderGUIItem item)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (item is PropertyToggleBlockItem && IsExplicitlyDisabled(item))
|
||||
{
|
||||
ExecuteResetChildren(item);
|
||||
item.CheckIsPropertyModified(true);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < item.ChildrenItemList.Count; i++)
|
||||
{
|
||||
ResetDisabledFeatureChildren(item.ChildrenItemList[i]);
|
||||
}
|
||||
|
||||
item.CheckIsPropertyModified(true);
|
||||
}
|
||||
|
||||
private static bool IsExplicitlyDisabled(ShaderGUIItem item)
|
||||
{
|
||||
MaterialProperty property = item.PropertyInfo?.Property;
|
||||
return property != null &&
|
||||
!property.hasMixedValue &&
|
||||
property.floatValue <= 0.5f;
|
||||
}
|
||||
|
||||
private static void ExecuteResetChildren(ShaderGUIItem item)
|
||||
{
|
||||
for (int i = 0; i < item.ChildrenItemList.Count; i++)
|
||||
{
|
||||
ExecuteResetSubtree(item.ChildrenItemList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ExecuteResetSubtree(ShaderGUIItem item)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
item.ExecuteReset(true);
|
||||
for (int i = 0; i < item.ChildrenItemList.Count; i++)
|
||||
{
|
||||
ExecuteResetSubtree(item.ChildrenItemList[i]);
|
||||
}
|
||||
|
||||
item.CheckIsPropertyModified(true);
|
||||
}
|
||||
|
||||
private void ResetSpecialUVChannelValues()
|
||||
{
|
||||
float defaultValue = GetDefaultFloat("_SpecialUVChannelMode", 0f);
|
||||
SetFloatProperty("_SpecialUVChannelMode", defaultValue);
|
||||
|
||||
bool useTexcoord1 = Mathf.RoundToInt(defaultValue) == 0;
|
||||
for (int i = 0; i < _rootItem.ShaderFlags.Count; i++)
|
||||
{
|
||||
ShaderFlagsBase flags = _rootItem.ShaderFlags[i];
|
||||
if (useTexcoord1)
|
||||
{
|
||||
flags.SetFlagBits(NBShaderFlags.FLAG_BIT_PARTICLE_1_USE_TEXCOORD1, index: 1);
|
||||
flags.ClearFlagBits(NBShaderFlags.FLAG_BIT_PARTICLE_1_USE_TEXCOORD2, index: 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
flags.ClearFlagBits(NBShaderFlags.FLAG_BIT_PARTICLE_1_USE_TEXCOORD1, index: 1);
|
||||
flags.SetFlagBits(NBShaderFlags.FLAG_BIT_PARTICLE_1_USE_TEXCOORD2, index: 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetTwirlValues()
|
||||
{
|
||||
float enabled = GetDefaultFloat("_UTwirlEnabled", 0f);
|
||||
SetFloatProperty("_UTwirlEnabled", enabled);
|
||||
SetVectorProperty("_TWParameter", GetDefaultVector("_TWParameter", new Vector4(0.5f, 0.5f, 0f, 0f)));
|
||||
SetFloatProperty("_TWStrength", GetDefaultFloat("_TWStrength", 1f));
|
||||
ApplyFlag(NBShaderFlags.FLAG_BIT_PARTICLE_UTWIRL_ON, enabled > 0.5f, 0);
|
||||
}
|
||||
|
||||
private void ResetPolarValues()
|
||||
{
|
||||
float enabled = GetDefaultFloat("_PolarCoordinatesEnabled", 0f);
|
||||
SetFloatProperty("_PolarCoordinatesEnabled", enabled);
|
||||
SetVectorProperty("_PCCenter", GetDefaultVector("_PCCenter", new Vector4(0.5f, 0.5f, 1f, 0f)));
|
||||
ApplyFlag(NBShaderFlags.FLAG_BIT_PARTICLE_POLARCOORDINATES_ON, enabled > 0.5f, 0);
|
||||
}
|
||||
|
||||
private void CollapseAll()
|
||||
{
|
||||
RecordAllMaterials(UndoText("collapseAll", "折叠所有控件"));
|
||||
foreach (KeyValuePair<string, ShaderPropertyInfo> pair in _rootItem.PropertyInfoDic)
|
||||
{
|
||||
if (!pair.Key.EndsWith("FoldOut", StringComparison.Ordinal))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
MaterialProperty property = pair.Value.Property;
|
||||
ShaderPropertyType propertyType = ShaderGUIUnityCompat.GetPropertyType(property);
|
||||
if (propertyType == ShaderPropertyType.Float ||
|
||||
propertyType == ShaderPropertyType.Range)
|
||||
{
|
||||
property.floatValue = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
MarkAllMaterialsDirty();
|
||||
UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
|
||||
}
|
||||
|
||||
private void CleanUnusedTextureProperties(Material mat)
|
||||
{
|
||||
if (mat == null || mat.shader == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Shader shader = mat.shader;
|
||||
var shaderTexProps = new HashSet<string>();
|
||||
int count = shader.GetPropertyCount();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (shader.GetPropertyType(i) == ShaderPropertyType.Texture)
|
||||
{
|
||||
shaderTexProps.Add(shader.GetPropertyName(i));
|
||||
}
|
||||
}
|
||||
|
||||
string[] texturePropertyNames = mat.GetTexturePropertyNames();
|
||||
foreach (string propertyName in texturePropertyNames)
|
||||
{
|
||||
if (shaderTexProps.Contains(propertyName) || mat.GetTexture(propertyName) == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
mat.SetTexture(propertyName, null);
|
||||
Debug.LogFormat(
|
||||
mat,
|
||||
NBShaderInspectorLocalization.Get(
|
||||
"inspector.toolbar.cleanUnusedTextures.log",
|
||||
"清理 {0} 的无效贴图属性: {1}"),
|
||||
mat.name,
|
||||
propertyName);
|
||||
}
|
||||
}
|
||||
|
||||
private void FinishMaterialMutation()
|
||||
{
|
||||
_rootItem.Context?.Refresh();
|
||||
_rootItem.SyncService?.SyncMaterialState();
|
||||
_rootItem.SyncService?.NotifyKeywordsMayHaveChanged();
|
||||
_rootItem.Context?.Refresh();
|
||||
MarkAllMaterialsDirty();
|
||||
UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
|
||||
}
|
||||
|
||||
private void FinishFeatureTierMutation()
|
||||
{
|
||||
_rootItem.Context?.Refresh();
|
||||
_rootItem.SyncService?.NotifyKeywordsMayHaveChanged();
|
||||
_rootItem.Context?.Refresh();
|
||||
MarkAllMaterialsDirty();
|
||||
UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
|
||||
}
|
||||
|
||||
private void ApplyFlag(int flagBits, bool enabled, int flagIndex)
|
||||
{
|
||||
for (int i = 0; i < _rootItem.ShaderFlags.Count; i++)
|
||||
{
|
||||
ShaderFlagsBase flags = _rootItem.ShaderFlags[i];
|
||||
if (enabled)
|
||||
{
|
||||
flags.SetFlagBits(flagBits, index: flagIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
flags.ClearFlagBits(flagBits, index: flagIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private float GetDefaultFloat(string propertyName, float fallback)
|
||||
{
|
||||
return _rootItem.PropertyInfoDic.TryGetValue(propertyName, out ShaderPropertyInfo info)
|
||||
? _rootItem.Shader.GetPropertyDefaultFloatValue(info.Index)
|
||||
: fallback;
|
||||
}
|
||||
|
||||
private Vector4 GetDefaultVector(string propertyName, Vector4 fallback)
|
||||
{
|
||||
return _rootItem.PropertyInfoDic.TryGetValue(propertyName, out ShaderPropertyInfo info)
|
||||
? _rootItem.Shader.GetPropertyDefaultVectorValue(info.Index)
|
||||
: fallback;
|
||||
}
|
||||
|
||||
private void SetFloatProperty(string propertyName, float value)
|
||||
{
|
||||
if (_rootItem.PropertyInfoDic.TryGetValue(propertyName, out ShaderPropertyInfo info))
|
||||
{
|
||||
info.Property.floatValue = value;
|
||||
}
|
||||
|
||||
foreach (Material mat in Materials)
|
||||
{
|
||||
if (mat.HasProperty(propertyName))
|
||||
{
|
||||
mat.SetFloat(propertyName, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetVectorProperty(string propertyName, Vector4 value)
|
||||
{
|
||||
if (_rootItem.PropertyInfoDic.TryGetValue(propertyName, out ShaderPropertyInfo info))
|
||||
{
|
||||
info.Property.vectorValue = value;
|
||||
}
|
||||
|
||||
foreach (Material mat in Materials)
|
||||
{
|
||||
if (mat.HasProperty(propertyName))
|
||||
{
|
||||
mat.SetVector(propertyName, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RecordAllMaterials(string undoName)
|
||||
{
|
||||
var objects = new List<UnityEngine.Object>();
|
||||
foreach (Material mat in Materials)
|
||||
{
|
||||
objects.Add(mat);
|
||||
}
|
||||
|
||||
if (objects.Count > 0)
|
||||
{
|
||||
Undo.RecordObjects(objects.ToArray(), undoName);
|
||||
}
|
||||
}
|
||||
|
||||
private void MarkAllMaterialsDirty()
|
||||
{
|
||||
foreach (Material mat in Materials)
|
||||
{
|
||||
EditorUtility.SetDirty(mat);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<Material> Materials
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_rootItem.Mats == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _rootItem.Mats.Count; i++)
|
||||
{
|
||||
Material mat = _rootItem.Mats[i];
|
||||
if (mat != null)
|
||||
{
|
||||
yield return mat;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Material MainMaterial
|
||||
{
|
||||
get
|
||||
{
|
||||
return _rootItem.Mats != null && _rootItem.Mats.Count > 0 ? _rootItem.Mats[0] : null;
|
||||
}
|
||||
}
|
||||
|
||||
private static GUIContent IconContent(string iconName, string key, string fallbackTooltip)
|
||||
{
|
||||
EnsureToolbarContentLanguage();
|
||||
if (IconContentCache.TryGetValue(key, out GUIContent cachedContent))
|
||||
{
|
||||
return cachedContent;
|
||||
}
|
||||
|
||||
GUIContent icon = EditorGUIUtility.IconContent(iconName);
|
||||
var content = new GUIContent(icon != null ? icon.image : null, Tip(key, fallbackTooltip));
|
||||
IconContentCache[key] = content;
|
||||
return content;
|
||||
}
|
||||
|
||||
private static void EnsureToolbarContentLanguage()
|
||||
{
|
||||
string currentLanguage = NBShaderInspectorLocalization.CurrentLanguage;
|
||||
if (string.Equals(s_ToolbarContentLanguage, currentLanguage, StringComparison.Ordinal))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
s_ToolbarContentLanguage = currentLanguage;
|
||||
s_SettingsContent = null;
|
||||
IconContentCache.Clear();
|
||||
TextContentCache.Clear();
|
||||
}
|
||||
|
||||
private static GUIContent TextContent(string key, string fallbackLabel, string fallbackTooltip)
|
||||
{
|
||||
EnsureToolbarContentLanguage();
|
||||
if (TextContentCache.TryGetValue(key, out GUIContent cachedContent))
|
||||
{
|
||||
return cachedContent;
|
||||
}
|
||||
|
||||
cachedContent = NBShaderInspectorLocalization.MakeInspectorContent("toolbar." + key, fallbackLabel, fallbackTooltip);
|
||||
TextContentCache[key] = cachedContent;
|
||||
return cachedContent;
|
||||
}
|
||||
|
||||
private static string Label(string key, string fallback)
|
||||
{
|
||||
return NBShaderInspectorLocalization.Get("inspector.toolbar." + key + ".label", fallback);
|
||||
}
|
||||
|
||||
private static string Tip(string key, string fallback)
|
||||
{
|
||||
return NBShaderInspectorLocalization.Get("inspector.toolbar." + key + ".tip", fallback);
|
||||
}
|
||||
|
||||
private static string UndoText(string key, string fallback)
|
||||
{
|
||||
return NBShaderInspectorLocalization.Get("inspector.toolbar." + key + ".undo", fallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0518e5ab9884c0e4d9b2c5ba352090ff
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user