您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
867 行
39 KiB
867 行
39 KiB
using System;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
|
|
using System.Linq;
|
|
|
|
namespace UnityEditor.Experimental.Rendering.HDPipeline
|
|
{
|
|
internal class LayeredLitGUI : LitGUI
|
|
{
|
|
public enum LayerUVBaseMapping
|
|
{
|
|
UV0,
|
|
UV1,
|
|
UV2,
|
|
UV3,
|
|
Planar,
|
|
Triplanar,
|
|
}
|
|
|
|
public enum VertexColorMode
|
|
{
|
|
None,
|
|
Multiply,
|
|
Add
|
|
}
|
|
|
|
private class StylesLayer
|
|
{
|
|
public readonly GUIContent[] layerLabels =
|
|
{
|
|
new GUIContent("Main layer"),
|
|
new GUIContent("Layer 1"),
|
|
new GUIContent("Layer 2"),
|
|
new GUIContent("Layer 3"),
|
|
};
|
|
|
|
public readonly GUIStyle[] layerLabelColors =
|
|
{
|
|
new GUIStyle(EditorStyles.label),
|
|
new GUIStyle(EditorStyles.label),
|
|
new GUIStyle(EditorStyles.label),
|
|
new GUIStyle(EditorStyles.label)
|
|
};
|
|
|
|
public readonly GUIContent materialLayerText = new GUIContent("Material");
|
|
public readonly GUIContent syncButtonText = new GUIContent("Re-Synchronize Layers", "Re-synchronize all layers's properties with the referenced Material");
|
|
public readonly GUIContent layersText = new GUIContent("Layers");
|
|
public readonly GUIContent emissiveText = new GUIContent("Emissive");
|
|
public readonly GUIContent layerMapMaskText = new GUIContent("Layer Mask", "Layer mask");
|
|
public readonly GUIContent vertexColorModeText = new GUIContent("Vertex Color Mode", "Mode multiply: vertex color is multiply with the mask. Mode additive: vertex color values are remapped between -1 and 1 and added to the mask (neutral at 0.5 vertex color).");
|
|
public readonly GUIContent layerCountText = new GUIContent("Layer Count", "Number of layers.");
|
|
public readonly GUIContent layerTilingText = new GUIContent("Tiling", "Tiling factor applied to UVSet");
|
|
public readonly GUIContent layerTexWorldScaleText = new GUIContent("Tiling", "Tiling factor applied to Planar/Trilinear mapping");
|
|
public readonly GUIContent UVBaseText = new GUIContent("Base UV Mapping", "Base UV Mapping mode of the layer.");
|
|
public readonly GUIContent UVDetailText = new GUIContent("Detail UV Mapping", "Detail UV Mapping mode of the layer.");
|
|
public readonly GUIContent mainLayerInfluenceText = new GUIContent("Main layer influence", "Main layer influence.");
|
|
public readonly GUIContent densityOpacityInfluenceText = new GUIContent("Density / Opacity", "Density / Opacity");
|
|
public readonly GUIContent useHeightBasedBlendText = new GUIContent("Use Height Based Blend", "Layer will be blended with the underlying layer based on the height.");
|
|
public readonly GUIContent useDensityModeModeText = new GUIContent("Use Density Mode", "Enable density mode");
|
|
public readonly GUIContent useMainLayerInfluenceModeText = new GUIContent("Main Layer Influence", "Switch between regular layers mode and base/layers mode");
|
|
public readonly GUIContent heightFactorText = new GUIContent("Height Multiplier", "Scale applied to the height of the layer.");
|
|
public readonly GUIContent heightControlText = new GUIContent("Height control");
|
|
|
|
public readonly GUIContent heightCenterOffsetText = new GUIContent("Height Center Offset", "Offset applied to the center of the height of the layer.");
|
|
public readonly GUIContent blendUsingHeight = new GUIContent("Blend Using Height", "Blend Layers using height.");
|
|
public readonly GUIContent inheritBaseColorThresholdText = new GUIContent("Threshold", "Inherit the base color from the base layer.");
|
|
public readonly GUIContent minimumOpacityText = new GUIContent("Minimum Opacity", "Minimum Opacity.");
|
|
public readonly GUIContent opacityAsDensityText = new GUIContent("Use Opacity as Density", "Use Opacity as Density.");
|
|
public readonly GUIContent inheritBaseNormalText = new GUIContent("Normal influence", "Inherit the normal from the base layer.");
|
|
public readonly GUIContent inheritBaseHeightText = new GUIContent("Heightmap influence", "Inherit the height from the base layer.");
|
|
public readonly GUIContent inheritBaseColorText = new GUIContent("BaseColor influence", "Inherit the base color from the base layer.");
|
|
public StylesLayer()
|
|
{
|
|
layerLabelColors[0].normal.textColor = Color.white;
|
|
layerLabelColors[1].normal.textColor = Color.red;
|
|
layerLabelColors[2].normal.textColor = Color.green;
|
|
layerLabelColors[3].normal.textColor = Color.blue;
|
|
}
|
|
|
|
}
|
|
|
|
static StylesLayer s_Styles = null;
|
|
private static StylesLayer styles { get { if (s_Styles == null) s_Styles = new StylesLayer(); return s_Styles; } }
|
|
|
|
// Needed for json serialization to work
|
|
[Serializable]
|
|
internal struct SerializeableGUIDs
|
|
{
|
|
public string[] GUIDArray;
|
|
}
|
|
|
|
const int kMaxLayerCount = 4;
|
|
const int kSyncButtonWidth = 58;
|
|
|
|
Material[] m_MaterialLayers = new Material[kMaxLayerCount];
|
|
|
|
MaterialProperty layerMaskMap = null;
|
|
const string kLayerMaskMap = "_LayerMaskMap";
|
|
MaterialProperty vertexColorMode = null;
|
|
const string kVertexColorMode = "_VertexColorMode";
|
|
MaterialProperty layerCount = null;
|
|
const string kLayerCount = "_LayerCount";
|
|
MaterialProperty[] layerTexWorldScale = new MaterialProperty[kMaxLayerCount];
|
|
MaterialProperty[] layerUVBase = new MaterialProperty[kMaxLayerCount];
|
|
MaterialProperty[] layerUVMappingMask = new MaterialProperty[kMaxLayerCount];
|
|
MaterialProperty[] layerUVMappingPlanar = new MaterialProperty[kMaxLayerCount];
|
|
MaterialProperty[] layerUVDetail = new MaterialProperty[kMaxLayerCount];
|
|
MaterialProperty[] layerUVDetailsMappingMask = new MaterialProperty[kMaxLayerCount];
|
|
|
|
const string kLayerTiling = "_LayerTiling";
|
|
MaterialProperty[] layerTiling = new MaterialProperty[kMaxLayerCount];
|
|
const string kkUseMainLayerInfluence = "_UseMainLayerInfluence";
|
|
MaterialProperty useMainLayerInfluence = null;
|
|
const string kUseHeightBasedBlend = "_UseHeightBasedBlend";
|
|
MaterialProperty useHeightBasedBlend = null;
|
|
|
|
const string kUseDensityMode = "_UseDensityMode";
|
|
MaterialProperty useDensityMode = null;
|
|
|
|
const string kOpacityAsDensity = "_OpacityAsDensity";
|
|
MaterialProperty[] opacityAsDensity = new MaterialProperty[kMaxLayerCount];
|
|
const string kMinimumOpacity = "_MinimumOpacity";
|
|
MaterialProperty[] minimumOpacity = new MaterialProperty[kMaxLayerCount];
|
|
|
|
const string kHeightFactor = "_HeightFactor";
|
|
MaterialProperty[] heightFactor = new MaterialProperty[kMaxLayerCount];
|
|
const string kHeightCenterOffset = "_HeightCenterOffset";
|
|
MaterialProperty[] heightCenterOffset = new MaterialProperty[kMaxLayerCount];
|
|
const string kBlendUsingHeight = "_BlendUsingHeight";
|
|
MaterialProperty[] blendUsingHeight = new MaterialProperty[kMaxLayerCount - 1];
|
|
|
|
// influence
|
|
const string kInheritBaseNormal = "_InheritBaseNormal";
|
|
MaterialProperty[] inheritBaseNormal = new MaterialProperty[kMaxLayerCount - 1];
|
|
const string kInheritBaseHeight = "_InheritBaseHeight";
|
|
MaterialProperty[] inheritBaseHeight = new MaterialProperty[kMaxLayerCount - 1];
|
|
const string kInheritBaseColor = "_InheritBaseColor";
|
|
MaterialProperty[] inheritBaseColor = new MaterialProperty[kMaxLayerCount - 1];
|
|
const string kInheritBaseColorThreshold = "_InheritBaseColorThreshold";
|
|
MaterialProperty[] inheritBaseColorThreshold = new MaterialProperty[kMaxLayerCount - 1];
|
|
|
|
MaterialProperty layerEmissiveColor = null;
|
|
MaterialProperty layerEmissiveColorMap = null;
|
|
MaterialProperty layerEmissiveIntensity = null;
|
|
|
|
override protected void FindMaterialProperties(MaterialProperty[] props)
|
|
{
|
|
FindMaterialOptionProperties(props);
|
|
|
|
layerMaskMap = FindProperty(kLayerMaskMap, props);
|
|
layerCount = FindProperty(kLayerCount, props);
|
|
vertexColorMode = FindProperty(kVertexColorMode, props);
|
|
|
|
useMainLayerInfluence = FindProperty(kkUseMainLayerInfluence, props);
|
|
useHeightBasedBlend = FindProperty(kUseHeightBasedBlend, props);
|
|
useDensityMode = FindProperty(kUseDensityMode, props);
|
|
|
|
for (int i = 0; i < kMaxLayerCount; ++i)
|
|
{
|
|
layerTexWorldScale[i] = FindProperty(string.Format("{0}{1}", kTexWorldScale, i), props);
|
|
layerUVBase[i] = FindProperty(string.Format("{0}{1}", kUVBase, i), props);
|
|
layerUVMappingMask[i] = FindProperty(string.Format("{0}{1}", kUVMappingMask, i), props);
|
|
layerUVMappingPlanar[i] = FindProperty(string.Format("{0}{1}", kUVMappingPlanar, i), props);
|
|
layerUVDetail[i] = FindProperty(string.Format("{0}{1}", kUVDetail, i), props);
|
|
layerUVDetailsMappingMask[i] = FindProperty(string.Format("{0}{1}", kUVDetailsMappingMask, i), props);
|
|
layerTiling[i] = FindProperty(string.Format("{0}{1}", kLayerTiling, i), props);
|
|
|
|
minimumOpacity[i] = FindProperty(string.Format("{0}{1}", kMinimumOpacity, i), props);
|
|
opacityAsDensity[i] = FindProperty(string.Format("{0}{1}", kOpacityAsDensity, i), props);
|
|
heightFactor[i] = FindProperty(string.Format("{0}{1}", kHeightFactor, i), props);
|
|
heightCenterOffset[i] = FindProperty(string.Format("{0}{1}", kHeightCenterOffset, i), props);
|
|
|
|
if(i != 0)
|
|
{
|
|
blendUsingHeight[i - 1] = FindProperty(string.Format("{0}{1}", kBlendUsingHeight, i), props);
|
|
inheritBaseNormal[i - 1] = FindProperty(string.Format("{0}{1}", kInheritBaseNormal, i), props);
|
|
inheritBaseHeight[i - 1] = FindProperty(string.Format("{0}{1}", kInheritBaseHeight, i), props);
|
|
inheritBaseColor[i - 1] = FindProperty(string.Format("{0}{1}", kInheritBaseColor, i), props);
|
|
inheritBaseColorThreshold[i - 1] = FindProperty(string.Format("{0}{1}", kInheritBaseColorThreshold, i), props);
|
|
}
|
|
}
|
|
|
|
layerEmissiveColor = FindProperty(kEmissiveColor, props);
|
|
layerEmissiveColorMap = FindProperty(kEmissiveColorMap, props);
|
|
layerEmissiveIntensity = FindProperty(kEmissiveIntensity, props);
|
|
}
|
|
|
|
int numLayer
|
|
{
|
|
set { layerCount.floatValue = (float)value; }
|
|
get { return (int)layerCount.floatValue; }
|
|
}
|
|
|
|
public static void SynchronizeAllLayers(Material material)
|
|
{
|
|
int layerCount = (int)material.GetFloat("_LayerCount");
|
|
AssetImporter materialImporter = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(material.GetInstanceID()));
|
|
|
|
Material[] layers = null;
|
|
InitializeMaterialLayers(materialImporter, ref layers);
|
|
for (int i = 0 ; i < layerCount ; ++i)
|
|
{
|
|
SynchronizeLayerProperties(material, layers, i);
|
|
}
|
|
}
|
|
|
|
void SynchronizeAllLayersProperties()
|
|
{
|
|
for (int i = 0; i < numLayer; ++i)
|
|
{
|
|
SynchronizeLayerProperties(m_MaterialEditor.target as Material, m_MaterialLayers, i);
|
|
}
|
|
}
|
|
|
|
static void SynchronizeLayerProperties(Material material, Material[] layers, int layerIndex)
|
|
{
|
|
string[] exclusionList = { kTexWorldScale, kUVBase, kUVMappingMask, kUVDetail, kUVMappingPlanar, kUVDetailsMappingMask };
|
|
|
|
Material layerMaterial = layers[layerIndex];
|
|
|
|
if (layerMaterial != null)
|
|
{
|
|
Shader layerShader = layerMaterial.shader;
|
|
int propertyCount = ShaderUtil.GetPropertyCount(layerShader);
|
|
for (int i = 0; i < propertyCount; ++i)
|
|
{
|
|
string propertyName = ShaderUtil.GetPropertyName(layerShader, i);
|
|
string layerPropertyName = propertyName + layerIndex;
|
|
|
|
if(!exclusionList.Contains(propertyName))
|
|
{
|
|
if (material.HasProperty(layerPropertyName))
|
|
{
|
|
ShaderUtil.ShaderPropertyType type = ShaderUtil.GetPropertyType(layerShader, i);
|
|
switch (type)
|
|
{
|
|
case ShaderUtil.ShaderPropertyType.Color:
|
|
{
|
|
material.SetColor(layerPropertyName, layerMaterial.GetColor(propertyName));
|
|
break;
|
|
}
|
|
case ShaderUtil.ShaderPropertyType.Float:
|
|
case ShaderUtil.ShaderPropertyType.Range:
|
|
{
|
|
material.SetFloat(layerPropertyName, layerMaterial.GetFloat(propertyName));
|
|
break;
|
|
}
|
|
case ShaderUtil.ShaderPropertyType.Vector:
|
|
{
|
|
material.SetVector(layerPropertyName, layerMaterial.GetVector(propertyName));
|
|
break;
|
|
}
|
|
case ShaderUtil.ShaderPropertyType.TexEnv:
|
|
{
|
|
material.SetTexture(layerPropertyName, layerMaterial.GetTexture(propertyName));
|
|
material.SetTextureOffset(layerPropertyName, layerMaterial.GetTextureOffset(propertyName));
|
|
material.SetTextureScale(layerPropertyName, layerMaterial.GetTextureScale(propertyName));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void InitializeMaterialLayers(AssetImporter materialImporter, ref Material[] layers)
|
|
{
|
|
if (materialImporter.userData != string.Empty)
|
|
{
|
|
SerializeableGUIDs layersGUID = JsonUtility.FromJson<SerializeableGUIDs>(materialImporter.userData);
|
|
if (layersGUID.GUIDArray.Length > 0)
|
|
{
|
|
layers = new Material[layersGUID.GUIDArray.Length];
|
|
for (int i = 0; i < layersGUID.GUIDArray.Length; ++i)
|
|
{
|
|
layers[i] = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(layersGUID.GUIDArray[i]), typeof(Material)) as Material;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SaveMaterialLayers(AssetImporter materialImporter)
|
|
{
|
|
SerializeableGUIDs layersGUID;
|
|
layersGUID.GUIDArray = new string[m_MaterialLayers.Length];
|
|
for (int i = 0; i < m_MaterialLayers.Length; ++i)
|
|
{
|
|
if (m_MaterialLayers[i] != null)
|
|
layersGUID.GUIDArray[i] = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(m_MaterialLayers[i].GetInstanceID()));
|
|
}
|
|
|
|
materialImporter.userData = JsonUtility.ToJson(layersGUID);
|
|
}
|
|
|
|
bool CheckInputOptionConsistency(string optionName, string[] shortNames, ref string outValueNames)
|
|
{
|
|
bool result = true;
|
|
outValueNames = "";
|
|
for (int i = 0; i < numLayer; ++i)
|
|
{
|
|
Material layer = m_MaterialLayers[i];
|
|
if (layer != null)
|
|
{
|
|
int currentValue = (int)layer.GetFloat(optionName); // All options are in fact enums
|
|
Debug.Assert(currentValue < shortNames.Length);
|
|
outValueNames += shortNames[currentValue] + " ";
|
|
|
|
for (int j = i + 1; j < numLayer; ++j)
|
|
{
|
|
Material otherLayer = m_MaterialLayers[j];
|
|
if (otherLayer != null)
|
|
{
|
|
if (currentValue != (int)otherLayer.GetFloat(optionName))
|
|
{
|
|
result = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
outValueNames += "X ";
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool CheckInputFloatOptionConsistency(string optionName, ref string outValueNames)
|
|
{
|
|
bool result = true;
|
|
outValueNames = "";
|
|
for (int i = 0; i < numLayer; ++i)
|
|
{
|
|
Material layer = m_MaterialLayers[i];
|
|
if (layer != null)
|
|
{
|
|
float currentValue = layer.GetFloat(optionName);
|
|
|
|
for (int j = i + 1; j < numLayer; ++j)
|
|
{
|
|
Material otherLayer = m_MaterialLayers[j];
|
|
if (otherLayer != null)
|
|
{
|
|
if (currentValue != otherLayer.GetFloat(optionName))
|
|
{
|
|
result = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
outValueNames += "X ";
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool CheckInputMapConsistency(string mapName, ref string outValueNames)
|
|
{
|
|
bool result = true;
|
|
outValueNames = "";
|
|
for (int i = 0; i < numLayer; ++i)
|
|
{
|
|
Material layer = m_MaterialLayers[i];
|
|
if (layer != null)
|
|
{
|
|
bool currentValue = layer.GetTexture(mapName) != null;
|
|
outValueNames += (currentValue ? "Y" : "N") + " ";
|
|
|
|
for (int j = i + 1; j < numLayer; ++j)
|
|
{
|
|
Material otherLayer = m_MaterialLayers[j];
|
|
if (otherLayer != null)
|
|
{
|
|
bool otherValue = otherLayer.GetTexture(mapName) != null;
|
|
if (currentValue != otherValue)
|
|
{
|
|
result = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
outValueNames += "N ";
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void CheckLayerConsistency()
|
|
{
|
|
string optionValueNames = "";
|
|
// We need to check consistency between all layers.
|
|
// Each input options and each input maps can result in different #defines in the shader so all of them need to be consistent
|
|
// otherwise the result will be undetermined
|
|
|
|
// Input options consistency
|
|
string[] smoothnessSourceShortNames = { "Mask", "Albedo" };
|
|
string[] normalMapShortNames = { "Tan", "Obj" };
|
|
string[] detailModeShortNames = { "DNormal", "DAOHeight" };
|
|
|
|
string warningInputOptions = "";
|
|
if (!CheckInputOptionConsistency(kSmoothnessTextureChannel, smoothnessSourceShortNames, ref optionValueNames))
|
|
{
|
|
warningInputOptions += "Smoothness Source: " + optionValueNames + "\n";
|
|
}
|
|
if (!CheckInputOptionConsistency(kNormalMapSpace, normalMapShortNames, ref optionValueNames))
|
|
{
|
|
warningInputOptions += "Normal Map Space: " + optionValueNames + "\n";
|
|
}
|
|
if (!CheckInputFloatOptionConsistency(kEnablePerPixelDisplacement, ref optionValueNames))
|
|
{
|
|
warningInputOptions += "Per pixel displacement: " + optionValueNames + "\n";
|
|
}
|
|
if (!CheckInputOptionConsistency(kDetailMapMode, detailModeShortNames, ref optionValueNames))
|
|
{
|
|
warningInputOptions += "Detail Map Mode: " + optionValueNames + "\n";
|
|
}
|
|
|
|
if (warningInputOptions != string.Empty)
|
|
{
|
|
warningInputOptions = "Input Option Consistency Error:\n" + warningInputOptions;
|
|
}
|
|
|
|
// Check input maps consistency
|
|
string warningInputMaps = "";
|
|
|
|
if (!CheckInputMapConsistency(kNormalMap, ref optionValueNames))
|
|
{
|
|
warningInputMaps += "Normal Map: " + optionValueNames + "\n";
|
|
}
|
|
if (!CheckInputMapConsistency(kDetailMap, ref optionValueNames))
|
|
{
|
|
warningInputMaps += "Detail Map: " + optionValueNames + "\n";
|
|
}
|
|
if (!CheckInputMapConsistency(kMaskMap, ref optionValueNames))
|
|
{
|
|
warningInputMaps += "Mask Map: " + optionValueNames + "\n";
|
|
}
|
|
if (!CheckInputMapConsistency(kSpecularOcclusionMap, ref optionValueNames))
|
|
{
|
|
warningInputMaps += "Specular Occlusion Map: " + optionValueNames + "\n";
|
|
}
|
|
if (!CheckInputMapConsistency(kHeightMap, ref optionValueNames))
|
|
{
|
|
warningInputMaps += "Height Map: " + optionValueNames + "\n";
|
|
}
|
|
|
|
if (warningInputMaps != string.Empty)
|
|
{
|
|
warningInputMaps = "Input Maps Consistency Error:\n" + warningInputMaps;
|
|
if (warningInputOptions != string.Empty)
|
|
warningInputMaps = "\n" + warningInputMaps;
|
|
}
|
|
|
|
string warning = warningInputOptions + warningInputMaps;
|
|
if (warning != string.Empty)
|
|
{
|
|
EditorGUILayout.HelpBox(warning, MessageType.Error);
|
|
}
|
|
}
|
|
|
|
void SynchronizeInputOptions()
|
|
{
|
|
Material material = m_MaterialEditor.target as Material;
|
|
|
|
// We synchronize input options with the firsts non null Layer (all layers should have consistent options)
|
|
Material firstLayer = null;
|
|
int i = 0;
|
|
while (i < numLayer && !(firstLayer = m_MaterialLayers[i])) ++i;
|
|
|
|
if (firstLayer != null)
|
|
{
|
|
material.SetFloat(kSmoothnessTextureChannel, firstLayer.GetFloat(kSmoothnessTextureChannel));
|
|
material.SetFloat(kNormalMapSpace, firstLayer.GetFloat(kNormalMapSpace));
|
|
material.SetFloat(kEnablePerPixelDisplacement, firstLayer.GetFloat(kEnablePerPixelDisplacement));
|
|
// Force emissive to be emissive color
|
|
material.SetFloat(kEmissiveColorMode, (float)EmissiveColorMode.UseEmissiveColor);
|
|
}
|
|
}
|
|
|
|
bool DoLayerGUI(AssetImporter materialImporter, int layerIndex)
|
|
{
|
|
bool result = false;
|
|
|
|
Material material = m_MaterialEditor.target as Material;
|
|
|
|
bool mainLayerInfluenceEnable = useMainLayerInfluence.floatValue > 0.0f;
|
|
|
|
EditorGUILayout.LabelField(styles.layerLabels[layerIndex], styles.layerLabelColors[layerIndex]);
|
|
|
|
EditorGUI.indentLevel++;
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
m_MaterialLayers[layerIndex] = EditorGUILayout.ObjectField(styles.materialLayerText, m_MaterialLayers[layerIndex], typeof(Material), true) as Material;
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
Undo.RecordObject(materialImporter, "Change layer material");
|
|
SynchronizeLayerProperties(material, m_MaterialLayers, layerIndex);
|
|
result = true;
|
|
}
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
m_MaterialEditor.ShaderProperty(layerUVBase[layerIndex], styles.UVBaseText);
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
SynchronizeLayerProperties(material, m_MaterialLayers, layerIndex);
|
|
result = true;
|
|
}
|
|
|
|
if (((LayerUVBaseMapping)layerUVBase[layerIndex].floatValue == LayerUVBaseMapping.Planar) ||
|
|
((LayerUVBaseMapping)layerUVBase[layerIndex].floatValue == LayerUVBaseMapping.Triplanar))
|
|
{
|
|
EditorGUI.indentLevel++;
|
|
m_MaterialEditor.ShaderProperty(layerTexWorldScale[layerIndex], styles.layerTexWorldScaleText);
|
|
EditorGUI.indentLevel--;
|
|
}
|
|
else
|
|
{
|
|
EditorGUI.indentLevel++;
|
|
m_MaterialEditor.ShaderProperty(layerTiling[layerIndex], styles.layerTilingText);
|
|
EditorGUI.indentLevel--;
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
m_MaterialEditor.ShaderProperty(layerUVDetail[layerIndex], styles.UVDetailText);
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
SynchronizeLayerProperties(material, m_MaterialLayers, layerIndex);
|
|
result = true;
|
|
}
|
|
}
|
|
|
|
bool useDensityModeEnable = useDensityMode.floatValue != 0.0f;
|
|
if (useDensityModeEnable)
|
|
{
|
|
EditorGUILayout.LabelField(styles.densityOpacityInfluenceText, EditorStyles.boldLabel);
|
|
|
|
EditorGUI.indentLevel++;
|
|
m_MaterialEditor.ShaderProperty(opacityAsDensity[layerIndex], styles.opacityAsDensityText);
|
|
m_MaterialEditor.ShaderProperty(minimumOpacity[layerIndex], styles.minimumOpacityText);
|
|
EditorGUI.indentLevel--;
|
|
}
|
|
|
|
EditorGUILayout.LabelField(styles.heightControlText, EditorStyles.boldLabel);
|
|
|
|
EditorGUI.indentLevel++;
|
|
m_MaterialEditor.ShaderProperty(heightFactor[layerIndex], styles.heightFactorText);
|
|
m_MaterialEditor.ShaderProperty(heightCenterOffset[layerIndex], styles.heightCenterOffsetText);
|
|
EditorGUI.indentLevel--;
|
|
|
|
// influence
|
|
if (layerIndex > 0)
|
|
{
|
|
int paramIndex = layerIndex - 1;
|
|
|
|
bool heightBasedBlendEnable = useHeightBasedBlend.floatValue != 0.0f;
|
|
if (heightBasedBlendEnable)
|
|
{
|
|
EditorGUI.indentLevel++;
|
|
m_MaterialEditor.ShaderProperty(blendUsingHeight[paramIndex], styles.blendUsingHeight);
|
|
EditorGUI.indentLevel--;
|
|
}
|
|
|
|
if (mainLayerInfluenceEnable)
|
|
{
|
|
EditorGUILayout.LabelField(styles.mainLayerInfluenceText, EditorStyles.boldLabel);
|
|
|
|
EditorGUI.indentLevel++;
|
|
|
|
m_MaterialEditor.ShaderProperty(inheritBaseColor[paramIndex], styles.inheritBaseColorText);
|
|
EditorGUI.indentLevel++;
|
|
m_MaterialEditor.ShaderProperty(inheritBaseColorThreshold[paramIndex], styles.inheritBaseColorThresholdText);
|
|
EditorGUI.indentLevel--;
|
|
m_MaterialEditor.ShaderProperty(inheritBaseNormal[paramIndex], styles.inheritBaseNormalText);
|
|
// Main height influence is only available if the shader use the heightmap for displacement (per vertex or per level)
|
|
// We always display it as it can be tricky to know when per pixel displacement is enabled or not
|
|
m_MaterialEditor.ShaderProperty(inheritBaseHeight[paramIndex], styles.inheritBaseHeightText);
|
|
|
|
EditorGUI.indentLevel--;
|
|
}
|
|
}
|
|
|
|
EditorGUI.indentLevel--;
|
|
|
|
if(layerIndex == 0)
|
|
EditorGUILayout.Space();
|
|
|
|
return result;
|
|
}
|
|
|
|
bool DoLayersGUI(AssetImporter materialImporter)
|
|
{
|
|
Material material = m_MaterialEditor.target as Material;
|
|
|
|
bool layerChanged = false;
|
|
|
|
GUI.changed = false;
|
|
|
|
EditorGUI.indentLevel++;
|
|
GUILayout.Label(styles.layersText, EditorStyles.boldLabel);
|
|
|
|
EditorGUI.showMixedValue = layerCount.hasMixedValue;
|
|
EditorGUI.BeginChangeCheck();
|
|
int newLayerCount = EditorGUILayout.IntSlider(styles.layerCountText, (int)layerCount.floatValue, 2, 4);
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
Undo.RecordObject(material, "Change layer count");
|
|
layerCount.floatValue = (float)newLayerCount;
|
|
SynchronizeAllLayersProperties();
|
|
layerChanged = true;
|
|
}
|
|
|
|
m_MaterialEditor.TexturePropertySingleLine(styles.layerMapMaskText, layerMaskMap);
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
EditorGUI.showMixedValue = useMainLayerInfluence.hasMixedValue;
|
|
bool mainLayerModeInfluenceEnable = EditorGUILayout.Toggle(styles.useMainLayerInfluenceModeText, useMainLayerInfluence.floatValue > 0.0f);
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
useMainLayerInfluence.floatValue = mainLayerModeInfluenceEnable ? 1.0f : 0.0f;
|
|
}
|
|
|
|
m_MaterialEditor.ShaderProperty(vertexColorMode, styles.vertexColorModeText);
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
EditorGUI.showMixedValue = useDensityMode.hasMixedValue;
|
|
bool useDensityModeEnable = EditorGUILayout.Toggle(styles.useDensityModeModeText, useDensityMode.floatValue > 0.0f);
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
useDensityMode.floatValue = useDensityModeEnable ? 1.0f : 0.0f;
|
|
}
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
EditorGUI.showMixedValue = useHeightBasedBlend.hasMixedValue;
|
|
bool enabled = EditorGUILayout.Toggle(styles.useHeightBasedBlendText, useHeightBasedBlend.floatValue > 0.0f);
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
useHeightBasedBlend.floatValue = enabled ? 1.0f : 0.0f;
|
|
}
|
|
|
|
EditorGUILayout.Space();
|
|
|
|
for (int i = 0; i < numLayer; i++)
|
|
{
|
|
layerChanged |= DoLayerGUI(materialImporter, i);
|
|
}
|
|
|
|
EditorGUILayout.Space();
|
|
GUILayout.BeginHorizontal();
|
|
{
|
|
GUILayout.FlexibleSpace();
|
|
if (GUILayout.Button(styles.syncButtonText))
|
|
{
|
|
SynchronizeAllLayersProperties();
|
|
layerChanged = true;
|
|
}
|
|
}
|
|
GUILayout.EndHorizontal();
|
|
|
|
EditorGUI.indentLevel--;
|
|
|
|
layerChanged |= GUI.changed;
|
|
GUI.changed = false;
|
|
|
|
return layerChanged;
|
|
}
|
|
|
|
protected override void SetupMaterialKeywords(Material material)
|
|
{
|
|
SetupCommonOptionsKeywords(material);
|
|
SetupLayersKeywords(material);
|
|
|
|
// Find first non null layer
|
|
int i = 0;
|
|
while (i < numLayer && (m_MaterialLayers[i] == null)) ++i;
|
|
|
|
if (i < numLayer)
|
|
{
|
|
SetKeyword(material, "_NORMALMAP", material.GetTexture(kNormalMap + i));
|
|
SetKeyword(material, "_MASKMAP", material.GetTexture(kMaskMap + i));
|
|
SetKeyword(material, "_SPECULAROCCLUSIONMAP", material.GetTexture(kSpecularOcclusionMap + i));
|
|
SetKeyword(material, "_HEIGHTMAP", material.GetTexture(kHeightMap + i));
|
|
SetKeyword(material, "_DETAIL_MAP", material.GetTexture(kDetailMap + i));
|
|
|
|
SetKeyword(material, "_DETAIL_MAP_WITH_NORMAL", ((DetailMapMode)material.GetFloat(kDetailMapMode)) == DetailMapMode.DetailWithNormal);
|
|
bool perPixelDisplacement = material.GetFloat(kEnablePerPixelDisplacement) == 1.0;
|
|
SetKeyword(material, "_PER_PIXEL_DISPLACEMENT", perPixelDisplacement);
|
|
SetKeyword(material, "_NORMALMAP_TANGENT_SPACE", ((NormalMapSpace)material.GetFloat(kNormalMapSpace)) == NormalMapSpace.TangentSpace);
|
|
SetKeyword(material, "_SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A", ((SmoothnessMapChannel)material.GetFloat(kSmoothnessTextureChannel)) == SmoothnessMapChannel.AlbedoAlpha);
|
|
}
|
|
|
|
SetKeyword(material, "_EMISSIVE_COLOR_MAP", material.GetTexture(kEmissiveColorMap));
|
|
|
|
SetKeyword(material, "_MAIN_LAYER_INFLUENCE_MODE", material.GetFloat(kkUseMainLayerInfluence) != 0.0f);
|
|
|
|
VertexColorMode VCMode = (VertexColorMode)vertexColorMode.floatValue;
|
|
if (VCMode == VertexColorMode.Multiply)
|
|
{
|
|
SetKeyword(material, "_LAYER_MASK_VERTEX_COLOR_MUL", true);
|
|
SetKeyword(material, "_LAYER_MASK_VERTEX_COLOR_ADD", false);
|
|
}
|
|
else if (VCMode == VertexColorMode.Add)
|
|
{
|
|
SetKeyword(material, "_LAYER_MASK_VERTEX_COLOR_MUL", false);
|
|
SetKeyword(material, "_LAYER_MASK_VERTEX_COLOR_ADD", true);
|
|
}
|
|
else
|
|
{
|
|
SetKeyword(material, "_LAYER_MASK_VERTEX_COLOR_MUL", false);
|
|
SetKeyword(material, "_LAYER_MASK_VERTEX_COLOR_ADD", false);
|
|
}
|
|
|
|
bool useHeightBasedBlend = material.GetFloat(kUseHeightBasedBlend) != 0.0f;
|
|
SetKeyword(material, "_HEIGHT_BASED_BLEND", useHeightBasedBlend);
|
|
|
|
bool useDensityModeEnable = material.GetFloat(kUseDensityMode) != 0.0f;
|
|
SetKeyword(material, "_DENSITY_MODE", useDensityModeEnable);
|
|
|
|
|
|
// We have to check for each layer if the UV2 or UV3 is needed.
|
|
bool needUV3 = false;
|
|
bool needUV2 = false;
|
|
for (int layer = 0; layer < numLayer; ++layer)
|
|
{
|
|
string uvBase = string.Format("{0}{1}", kUVBase, layer);
|
|
string uvDetail = string.Format("{0}{1}", kUVDetail, layer);
|
|
|
|
if ( ((UVDetailMapping)material.GetFloat(uvDetail) == UVDetailMapping.UV2) ||
|
|
((LayerUVBaseMapping)material.GetFloat(uvBase) == LayerUVBaseMapping.UV2) )
|
|
{
|
|
needUV2 = true;
|
|
}
|
|
|
|
if ( ((UVDetailMapping)material.GetFloat(uvDetail) == UVDetailMapping.UV3) ||
|
|
((LayerUVBaseMapping)material.GetFloat(uvBase) == LayerUVBaseMapping.UV3) )
|
|
{
|
|
needUV3 = true;
|
|
break; // If we find it UV3 let's early out
|
|
}
|
|
}
|
|
|
|
if (needUV3)
|
|
{
|
|
material.DisableKeyword("_REQUIRE_UV2");
|
|
material.EnableKeyword("_REQUIRE_UV3");
|
|
}
|
|
else if (needUV2)
|
|
{
|
|
material.EnableKeyword("_REQUIRE_UV2");
|
|
material.DisableKeyword("_REQUIRE_UV3");
|
|
}
|
|
else
|
|
{
|
|
material.DisableKeyword("_REQUIRE_UV2");
|
|
material.DisableKeyword("_REQUIRE_UV3");
|
|
}
|
|
}
|
|
|
|
void SetupLayersKeywords(Material material)
|
|
{
|
|
if (numLayer == 4)
|
|
{
|
|
SetKeyword(material, "_LAYEREDLIT_4_LAYERS", true);
|
|
SetKeyword(material, "_LAYEREDLIT_3_LAYERS", false);
|
|
}
|
|
else if (numLayer == 3)
|
|
{
|
|
SetKeyword(material, "_LAYEREDLIT_4_LAYERS", false);
|
|
SetKeyword(material, "_LAYEREDLIT_3_LAYERS", true);
|
|
}
|
|
else
|
|
{
|
|
SetKeyword(material, "_LAYEREDLIT_4_LAYERS", false);
|
|
SetKeyword(material, "_LAYEREDLIT_3_LAYERS", false);
|
|
}
|
|
|
|
const string kLayerMappingTriplanar = "_LAYER_MAPPING_TRIPLANAR_";
|
|
|
|
for (int i = 0 ; i < numLayer; ++i)
|
|
{
|
|
// We setup the masking map based on the enum for each layer.
|
|
// using mapping mask allow to reduce the number of generated combination for a very small increase in ALU
|
|
string layerUVBaseParam = string.Format("{0}{1}", kUVBase, i);
|
|
LayerUVBaseMapping layerUVBaseMapping = (LayerUVBaseMapping)material.GetFloat(layerUVBaseParam);
|
|
string layerUVDetailParam = string.Format("{0}{1}", kUVDetail, i);
|
|
UVDetailMapping layerUVDetailMapping = (UVDetailMapping)material.GetFloat(layerUVDetailParam);
|
|
string currentLayerMappingTriplanar = string.Format("{0}{1}", kLayerMappingTriplanar, i);
|
|
|
|
float X, Y, Z, W;
|
|
X = (layerUVBaseMapping == LayerUVBaseMapping.UV0) ? 1.0f : 0.0f;
|
|
Y = (layerUVBaseMapping == LayerUVBaseMapping.UV1) ? 1.0f : 0.0f;
|
|
Z = (layerUVBaseMapping == LayerUVBaseMapping.UV2) ? 1.0f : 0.0f;
|
|
W = (layerUVBaseMapping == LayerUVBaseMapping.UV3) ? 1.0f : 0.0f;
|
|
layerUVMappingMask[i].colorValue = new Color(X, Y, Z, W);
|
|
layerUVMappingPlanar[i].floatValue = (layerUVBaseMapping == LayerUVBaseMapping.Planar) ? 1.0f : 0.0f;
|
|
|
|
SetKeyword(material, currentLayerMappingTriplanar, layerUVBaseMapping == LayerUVBaseMapping.Triplanar);
|
|
|
|
X = (layerUVDetailMapping == UVDetailMapping.UV0) ? 1.0f : 0.0f;
|
|
Y = (layerUVDetailMapping == UVDetailMapping.UV1) ? 1.0f : 0.0f;
|
|
Z = (layerUVDetailMapping == UVDetailMapping.UV2) ? 1.0f : 0.0f;
|
|
W = (layerUVDetailMapping == UVDetailMapping.UV3) ? 1.0f : 0.0f;
|
|
layerUVDetailsMappingMask[i].colorValue = new Color(X, Y, Z, W);
|
|
}
|
|
}
|
|
|
|
public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] props)
|
|
{
|
|
FindCommonOptionProperties(props);
|
|
FindMaterialProperties(props);
|
|
|
|
m_MaterialEditor = materialEditor;
|
|
|
|
m_MaterialEditor.serializedObject.Update();
|
|
|
|
Material material = m_MaterialEditor.target as Material;
|
|
AssetImporter materialImporter = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(material.GetInstanceID()));
|
|
|
|
InitializeMaterialLayers(materialImporter, ref m_MaterialLayers);
|
|
|
|
bool optionsChanged = false;
|
|
EditorGUI.BeginChangeCheck();
|
|
{
|
|
ShaderOptionsGUI();
|
|
EditorGUILayout.Space();
|
|
}
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
optionsChanged = true;
|
|
}
|
|
|
|
bool layerChanged = DoLayersGUI(materialImporter);
|
|
|
|
EditorGUILayout.Space();
|
|
GUILayout.Label(Styles.lightingText, EditorStyles.boldLabel);
|
|
EditorGUI.indentLevel++;
|
|
m_MaterialEditor.TexturePropertySingleLine(Styles.emissiveText, layerEmissiveColorMap, layerEmissiveColor);
|
|
m_MaterialEditor.ShaderProperty(layerEmissiveIntensity, Styles.emissiveIntensityText);
|
|
m_MaterialEditor.LightmapEmissionProperty(1);
|
|
m_MaterialEditor.ShaderProperty(horizonFade, Styles.horizonFadeText);
|
|
EditorGUI.indentLevel--;
|
|
|
|
CheckLayerConsistency();
|
|
|
|
if (layerChanged || optionsChanged)
|
|
{
|
|
SynchronizeInputOptions();
|
|
|
|
foreach (var obj in m_MaterialEditor.targets)
|
|
{
|
|
SetupMaterialKeywords((Material)obj);
|
|
}
|
|
|
|
SaveMaterialLayers(materialImporter);
|
|
}
|
|
|
|
m_MaterialEditor.serializedObject.ApplyModifiedProperties();
|
|
}
|
|
}
|
|
} // namespace UnityEditor
|