using System;
using UnityEngine;
using UnityEngine.Rendering.HighDefinition;
using System.Reflection;
using System.Linq.Expressions;
using System.Linq;
using UnityEngine.Rendering;
// Include material common properties names
using static UnityEngine.Rendering.HighDefinition.HDMaterialProperties;
namespace UnityEditor.Rendering.HighDefinition
{
///
/// The UI block that displays emission properties for materials.
///
public class EmissionUIBlock : MaterialUIBlock
{
// Max EV Value. Equals to LightUtils.ConvertLuminanceToEv(float.MaxValue)
// Literal value to avoid precision issue with max float and to be independent of ColorUtils.s_LightMeterCalibrationConstant.
static float s_MaxEvValue = 130.0f;
/// Options for emission block features. Use this to control which fields are visible.
[Flags]
public enum Features
{
/// Shows the minimal emission fields.
None = 0,
/// Shows the enable emission for GI field.
EnableEmissionForGI = 1 << 0,
/// Shows the multiply with base field.
MultiplyWithBase = 1 << 1,
/// Shows all the fields.
All = ~0
}
static Func GetLightingSettingsOrDefaultsFallback;
static EmissionUIBlock()
{
Type lightMappingType = typeof(Lightmapping);
var getLightingSettingsOrDefaultsFallbackInfo = lightMappingType.GetMethod("GetLightingSettingsOrDefaultsFallback", BindingFlags.Static | BindingFlags.NonPublic);
var getLightingSettingsOrDefaultsFallbackLambda = Expression.Lambda>(Expression.Call(null, getLightingSettingsOrDefaultsFallbackInfo));
GetLightingSettingsOrDefaultsFallback = getLightingSettingsOrDefaultsFallbackLambda.Compile();
}
internal class Styles
{
public static readonly GUIContent header = EditorGUIUtility.TrTextContent("Emission Inputs");
public static GUIContent emissiveMap = new GUIContent("Emissive Map", "Specifies the emissive color (RGB) of the Material.");
public static GUIContent albedoAffectEmissiveText = new GUIContent("Emission multiply with Base", "Specifies whether or not the emission color is multiplied by the albedo.");
public static GUIContent useEmissiveIntensityText = new GUIContent("Use Emission Intensity", "Specifies whether to use to a HDR color or a LDR color with a separate multiplier.");
public static GUIContent emissiveIntensityText = new GUIContent("Emission Intensity", "Emission intensity in provided Unit");
public static GUIContent emissiveIntensityFromHDRColorText = new GUIContent("The emission intensity is from the HDR color picker in luminance", "");
public static GUIContent emissiveExposureWeightText = new GUIContent("Exposure weight", "Controls how the camera exposure influences the perceived intensity of the emissivity. A weight of 0 means that the emissive intensity is calculated ignoring the exposure; increasing this weight progressively increases the influence of exposure on the final emissive value.");
public static GUIContent UVEmissiveMappingText = new GUIContent("Emission UV mapping", "");
public static GUIContent texWorldScaleText = new GUIContent("World Scale", "Sets the tiling factor HDRP applies to Planar/Trilinear mapping.");
public static GUIContent uvMappingSpace = new GUIContent("UV Mapping Space", "Sets the space for the input position used for Planar/Trilinear mapping.");
}
MaterialProperty emissiveColorLDR = null;
MaterialProperty emissiveExposureWeight = null;
MaterialProperty useEmissiveIntensity = null;
MaterialProperty emissiveIntensityUnit = null;
MaterialProperty emissiveIntensity = null;
MaterialProperty emissiveColor = null;
MaterialProperty emissiveColorMap = null;
MaterialProperty UVEmissive = null;
MaterialProperty TexWorldScaleEmissive = null;
const string kTexWorldScaleEmissive = "_TexWorldScaleEmissive";
MaterialProperty UVMappingMaskEmissive = null;
const string kUVMappingMaskEmissive = "_UVMappingMaskEmissive";
MaterialProperty albedoAffectEmissive = null;
const string kAlbedoAffectEmissive = "_AlbedoAffectEmissive";
MaterialProperty ObjectSpaceUVMappingEmissive = null;
const string kObjectSpaceUVMappingEmissive = "_ObjectSpaceUVMappingEmissive";
Features m_Features;
///
/// Constructs an EmissionUIBlock based on the parameters.
///
/// Bit index used to store the foldout state.
/// Features of the block.
public EmissionUIBlock(ExpandableBit expandableBit, Features features = Features.All)
: base(expandableBit, Styles.header)
{
m_Features = features;
}
///
/// Loads the material properties for the block.
///
public override void LoadMaterialProperties()
{
emissiveColor = FindProperty(kEmissiveColor);
emissiveColorMap = FindProperty(kEmissiveColorMap);
emissiveIntensityUnit = FindProperty(kEmissiveIntensityUnit);
emissiveIntensity = FindProperty(kEmissiveIntensity);
emissiveExposureWeight = FindProperty(kEmissiveExposureWeight);
emissiveColorLDR = FindProperty(kEmissiveColorLDR);
useEmissiveIntensity = FindProperty(kUseEmissiveIntensity);
albedoAffectEmissive = FindProperty(kAlbedoAffectEmissive);
UVEmissive = FindProperty(kUVEmissive);
TexWorldScaleEmissive = FindProperty(kTexWorldScaleEmissive);
UVMappingMaskEmissive = FindProperty(kUVMappingMaskEmissive);
ObjectSpaceUVMappingEmissive = FindProperty(kObjectSpaceUVMappingEmissive);
}
internal static void UpdateEmissiveColorLDRAndIntensityFromEmissiveColor(MaterialProperty emissiveColorLDR, MaterialProperty emissiveIntensity, MaterialProperty emissiveColor)
{
HDUtils.ConvertHDRColorToLDR(emissiveColor.colorValue, out var colorLDR, out var intensity);
emissiveIntensity.floatValue = intensity;
emissiveColorLDR.colorValue = colorLDR.gamma;
}
internal static void DoEmissiveIntensityGUI(MaterialEditor materialEditor, MaterialProperty emissiveIntensity, MaterialProperty emissiveIntensityUnit)
{
MaterialEditor.BeginProperty(emissiveIntensity);
MaterialEditor.BeginProperty(emissiveIntensityUnit);
bool unitIsMixed = emissiveIntensityUnit.hasMixedValue;
bool intensityIsMixed = unitIsMixed || emissiveIntensity.hasMixedValue;
float indent = 15 * EditorGUI.indentLevel;
const int k_ValueUnitSeparator = 2;
const int k_UnitWidth = 100;
Rect valueRect = EditorGUILayout.GetControlRect();
valueRect.width += indent - k_ValueUnitSeparator - k_UnitWidth;
Rect unitRect = valueRect;
unitRect.x += valueRect.width - indent + k_ValueUnitSeparator;
unitRect.width = k_UnitWidth + .5f;
{
EditorGUI.showMixedValue = intensityIsMixed;
EmissiveIntensityUnit unit = (EmissiveIntensityUnit)emissiveIntensityUnit.floatValue;
if (unitIsMixed)
{
using (new EditorGUI.DisabledScope(true))
materialEditor.ShaderProperty(valueRect, emissiveIntensity, Styles.emissiveIntensityText);
}
else
{
if (!intensityIsMixed && unit == EmissiveIntensityUnit.EV100)
{
float evValue = LightUnitUtils.NitsToEv100(emissiveIntensity.floatValue);
evValue = EditorGUI.FloatField(valueRect, Styles.emissiveIntensityText, evValue);
evValue = Mathf.Clamp(evValue, 0, s_MaxEvValue);
emissiveIntensity.floatValue = LightUnitUtils.Ev100ToNits(evValue);
}
else
{
EditorGUI.BeginChangeCheck();
materialEditor.ShaderProperty(valueRect, emissiveIntensity, Styles.emissiveIntensityText);
if (EditorGUI.EndChangeCheck())
emissiveIntensity.floatValue = Mathf.Clamp(emissiveIntensity.floatValue, 0, float.MaxValue);
}
}
EditorGUI.showMixedValue = emissiveIntensityUnit.hasMixedValue;
EditorGUI.BeginChangeCheck();
var newUnit = (EmissiveIntensityUnit)EditorGUI.EnumPopup(unitRect, unit);
if (EditorGUI.EndChangeCheck())
emissiveIntensityUnit.floatValue = (float)newUnit;
}
EditorGUI.showMixedValue = false;
MaterialEditor.EndProperty();
MaterialEditor.EndProperty();
}
///
/// GUI callback when the header is open
///
protected override void OnGUIOpen()
{
EditorGUI.BeginChangeCheck();
materialEditor.ShaderProperty(useEmissiveIntensity, Styles.useEmissiveIntensityText);
bool updateEmissiveColor = EditorGUI.EndChangeCheck();
// This flag allows us to track is a material has a non-null emission color. That would require us to enable the target pass
if (useEmissiveIntensity.floatValue == 0)
{
DoEmissiveTextureProperty(emissiveColor);
EditorGUILayout.HelpBox(Styles.emissiveIntensityFromHDRColorText.text, MessageType.Info, true);
}
else
{
if (updateEmissiveColor)
UpdateEmissiveColorLDRAndIntensityFromEmissiveColor(emissiveColorLDR, emissiveIntensity, emissiveColor);
DoEmissiveTextureProperty(emissiveColorLDR);
DoEmissiveIntensityGUI(materialEditor, emissiveIntensity, emissiveIntensityUnit);
}
materialEditor.ShaderProperty(emissiveExposureWeight, Styles.emissiveExposureWeightText);
if ((m_Features & Features.MultiplyWithBase) != 0)
materialEditor.ShaderProperty(albedoAffectEmissive, Styles.albedoAffectEmissiveText);
// Emission for GI?
if ((m_Features & Features.EnableEmissionForGI) != 0)
{
// Change the GI emission flag and fix it up with emissive as black if necessary.
materialEditor.LightmapEmissionFlagsProperty(0, true);
}
}
internal static void DoEmissiveTextureProperty(MaterialEditor materialEditor, MaterialProperty texture, MaterialProperty color)
{
materialEditor.TexturePropertySingleLine(Styles.emissiveMap, texture, color);
}
void DoEmissiveTextureProperty(MaterialProperty color)
{
DoEmissiveTextureProperty(materialEditor, emissiveColorMap, color);
if (materials.All(m => m.GetTexture(kEmissiveColorMap)))
{
EditorGUI.indentLevel++;
if (UVEmissive != null) // Unlit does not have UVEmissive
{
materialEditor.ShaderProperty(UVEmissive, Styles.UVEmissiveMappingText);
UVEmissiveMapping uvEmissiveMapping = (UVEmissiveMapping)UVEmissive.floatValue;
float X, Y, Z, W;
X = (uvEmissiveMapping == UVEmissiveMapping.UV0) ? 1.0f : 0.0f;
Y = (uvEmissiveMapping == UVEmissiveMapping.UV1) ? 1.0f : 0.0f;
Z = (uvEmissiveMapping == UVEmissiveMapping.UV2) ? 1.0f : 0.0f;
W = (uvEmissiveMapping == UVEmissiveMapping.UV3) ? 1.0f : 0.0f;
UVMappingMaskEmissive.colorValue = new Color(X, Y, Z, W);
if ((uvEmissiveMapping == UVEmissiveMapping.Planar) || (uvEmissiveMapping == UVEmissiveMapping.Triplanar))
{
materialEditor.ShaderProperty(ObjectSpaceUVMappingEmissive, Styles.uvMappingSpace);
materialEditor.ShaderProperty(TexWorldScaleEmissive, Styles.texWorldScaleText);
}
}
if (UVEmissive == null || (UVEmissiveMapping)UVEmissive.floatValue != UVEmissiveMapping.SameAsBase)
materialEditor.TextureScaleOffsetProperty(emissiveColorMap);
EditorGUI.indentLevel--;
}
}
}
}