using System.Collections.Generic;
using UnityEngine;
namespace NBShader
{
///
/// Runtime API for applying NBShader feature tiers to materials.
///
public static class NBShaderFeatureRuntime
{
///
/// Applies an NBShader feature tier to one material in place. Only materials whose shader name is
/// "Effects/NBShader" are processed. Managed Catalog keywords and shader passes are derived from
/// serialized material intent, then filtered by the target tier.
///
/// Material to process. Null materials are ignored.
///
/// Target tier. When null, Ultra is used. This overload does not load runtime settings, so it keeps all
/// managed Catalog keywords and pass features available.
///
///
/// If lower-tier shader variants have been stripped from the build, call this API before the material is
/// used for rendering. Otherwise Unity can request a missing variant and select a similar available variant.
///
public static void ApplyTier(Material material, NBShaderFeatureTier? tier = null)
{
ApplyTierInternal(material, null, tier);
}
///
/// Applies an NBShader feature tier using a user-loaded runtime settings asset.
/// Pass null for to resolve the tier from the settings quality mapping.
///
public static void ApplyTier(Material material, NBShaderFeatureRuntimeSettings settings, NBShaderFeatureTier? tier)
{
ApplyTierInternal(material, settings, tier);
}
///
/// Applies an NBShader feature tier to multiple materials in place. Only materials whose shader name is
/// "Effects/NBShader" are processed. Catalog-external keywords are left unchanged unless they are
/// explicitly tied to a managed NBShader feature.
///
/// Materials to process. Null collections and null entries are ignored.
///
/// Target tier. When null, Ultra is used. This overload does not load runtime settings, so it keeps all
/// managed Catalog keywords and pass features available.
///
///
/// If lower-tier shader variants have been stripped from the build, call this API before the materials are
/// used for rendering. Otherwise Unity can request a missing variant and select a similar available variant.
///
public static void ApplyTier(IEnumerable materials, NBShaderFeatureTier? tier = null)
{
ApplyTierInternal(materials, null, tier);
}
///
/// Applies an NBShader feature tier to multiple materials using a user-loaded runtime settings asset.
/// Pass null for to resolve the tier from the settings quality mapping.
///
public static void ApplyTier(IEnumerable materials, NBShaderFeatureRuntimeSettings settings, NBShaderFeatureTier? tier)
{
ApplyTierInternal(materials, settings, tier);
}
private static void ApplyTierInternal(IEnumerable materials, NBShaderFeatureRuntimeSettings settings, NBShaderFeatureTier? tier)
{
if (materials == null)
{
return;
}
foreach (Material material in materials)
{
ApplyTierInternal(material, settings, tier);
}
}
private static void ApplyTierInternal(Material material, NBShaderFeatureRuntimeSettings settings, NBShaderFeatureTier? tier)
{
if (!IsNBShaderMaterial(material))
{
return;
}
NBShaderFeatureTier resolvedTier = tier.HasValue ? tier.Value : ResolveTierFromQuality(settings);
HashSet allowed = settings != null
? settings.BuildAllowedSet(resolvedTier)
: BuildAllowAllCatalogKeywordSet();
HashSet allowedPassFeatures = settings != null
? settings.BuildAllowedPassFeatureSet(resolvedTier)
: null;
NBShaderMaterialIntentResult result = NBShaderMaterialIntentResolver.Resolve(material, resolvedTier, allowed, allowedPassFeatures);
ApplyResolvedIntent(material, result);
}
private static bool IsNBShaderMaterial(Material material)
{
return material != null
&& material.shader != null
&& material.shader.name == NBShaderFeatureCatalog.ShaderName;
}
private static void ApplyResolvedIntent(Material material, NBShaderMaterialIntentResult result)
{
if (material == null || result == null)
{
return;
}
var effectiveKeywords = new HashSet(result.effectiveKeywords);
for (int i = 0; i < NBShaderFeatureCatalog.RawKeywords.Length; i++)
{
string keyword = NBShaderFeatureCatalog.RawKeywords[i];
bool shouldEnable = effectiveKeywords.Contains(keyword);
if (material.IsKeywordEnabled(keyword) != shouldEnable)
{
if (shouldEnable)
{
material.EnableKeyword(keyword);
}
else
{
material.DisableKeyword(keyword);
}
}
}
bool evaluateShVertex = effectiveKeywords.Contains("_FX_LIGHT_MODE_SIX_WAY");
if (material.IsKeywordEnabled("EVALUATE_SH_VERTEX") != evaluateShVertex)
{
if (evaluateShVertex)
{
material.EnableKeyword("EVALUATE_SH_VERTEX");
}
else
{
material.DisableKeyword("EVALUATE_SH_VERTEX");
}
}
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);
}
}
}
private static NBShaderFeatureTier ResolveTierFromQuality(NBShaderFeatureRuntimeSettings settings)
{
if (settings == null)
{
return NBShaderFeatureTier.Ultra;
}
string qualityName = GetCurrentQualityName();
NBShaderFeatureTier tier;
return settings.TryGetTierForQualityName(qualityName, out tier) ? tier : NBShaderFeatureTier.Ultra;
}
private static string GetCurrentQualityName()
{
string[] names = QualitySettings.names;
int index = QualitySettings.GetQualityLevel();
if (names != null && index >= 0 && index < names.Length)
{
return names[index];
}
return string.Empty;
}
private static HashSet BuildAllowAllCatalogKeywordSet()
{
return new HashSet(NBShaderFeatureCatalog.RawKeywords);
}
}
}