您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
326 行
16 KiB
326 行
16 KiB
using System;
|
|
using UnityEngine;
|
|
using UnityEngine.Experimental.Rendering;
|
|
using UnityEngine.Experimental.Rendering.HDPipeline;
|
|
|
|
using System.Linq;
|
|
|
|
namespace UnityEditor.Experimental.Rendering.HDPipeline
|
|
{
|
|
public class TerrainLitGUI : LitGUI, ITerrainLayerCustomUI
|
|
{
|
|
private class StylesLayer
|
|
{
|
|
public readonly GUIContent enableHeightBlend = new GUIContent("Enable Height-based Blend", "Blend terrain layers based on height values.");
|
|
public readonly GUIContent heightTransition = new GUIContent("Height Transition", "Size in world units of the smooth transition between layers.");
|
|
public readonly GUIContent enableInstancedPerPixelNormal = new GUIContent("Enable Per-pixel Normal", "Enable per-pixel normal when the terrain uses instanced rendering.");
|
|
|
|
public readonly GUIContent diffuseTexture = new GUIContent("Diffuse");
|
|
public readonly GUIContent colorTint = new GUIContent("Color Tint");
|
|
public readonly GUIContent opacityAsDensity = new GUIContent("Opacity as Density", "Enable Density Blend");
|
|
public readonly GUIContent normalMapTexture = new GUIContent("Normal Map");
|
|
public readonly GUIContent normalScale = new GUIContent("Normal Scale");
|
|
public readonly GUIContent maskMapTexture = new GUIContent("Mask", "R: Metallic\nG: AO\nB: Height\nA: Smoothness");
|
|
public readonly GUIContent maskMapTextureWithoutHeight = new GUIContent("Mask Map", "R: Metallic\nG: AO\nA: Smoothness");
|
|
public readonly GUIContent channelRemapping = new GUIContent("Channel Remapping");
|
|
public readonly GUIContent defaultValues = new GUIContent("Channel Default Values");
|
|
public readonly GUIContent metallic = new GUIContent("R: Metallic");
|
|
public readonly GUIContent ao = new GUIContent("G: AO");
|
|
public readonly GUIContent height = new GUIContent("B: Height");
|
|
public readonly GUIContent heightParametrization = new GUIContent("Parametrization");
|
|
public readonly GUIContent heightAmplitude = new GUIContent("Amplitude (cm)");
|
|
public readonly GUIContent heightBase = new GUIContent("Base");
|
|
public readonly GUIContent heightMin = new GUIContent("Min (cm)");
|
|
public readonly GUIContent heightMax = new GUIContent("Max (cm)");
|
|
public readonly GUIContent heightCm = new GUIContent("B: Height (cm)");
|
|
public readonly GUIContent smoothness = new GUIContent("A: Smoothness");
|
|
}
|
|
|
|
static StylesLayer s_Styles = null;
|
|
private static StylesLayer styles { get { if (s_Styles == null) s_Styles = new StylesLayer(); return s_Styles; } }
|
|
|
|
public TerrainLitGUI()
|
|
{
|
|
}
|
|
|
|
MaterialProperty enableHeightBlend;
|
|
const string kEnableHeightBlend = "_EnableHeightBlend";
|
|
|
|
// Height blend
|
|
MaterialProperty heightTransition = null;
|
|
const string kHeightTransition = "_HeightTransition";
|
|
|
|
MaterialProperty enableInstancedPerPixelNormal = null;
|
|
const string kEnableInstancedPerPixelNormal = "_EnableInstancedPerPixelNormal";
|
|
|
|
protected override void FindMaterialProperties(MaterialProperty[] props)
|
|
{
|
|
enableHeightBlend = FindProperty(kEnableHeightBlend, props, false);
|
|
heightTransition = FindProperty(kHeightTransition, props, false);
|
|
enableInstancedPerPixelNormal = FindProperty(kEnableInstancedPerPixelNormal, props, false);
|
|
}
|
|
|
|
protected override bool ShouldEmissionBeEnabled(Material mat)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
protected override void SetupMaterialKeywordsAndPassInternal(Material material)
|
|
{
|
|
SetupMaterialKeywordsAndPass(material);
|
|
}
|
|
|
|
static public void SetupLayersMappingKeywords(Material material)
|
|
{
|
|
const string kLayerMappingPlanar = "_LAYER_MAPPING_PLANAR";
|
|
const string kLayerMappingTriplanar = "_LAYER_MAPPING_TRIPLANAR";
|
|
|
|
for (int i = 0; i < kMaxLayerCount; ++i)
|
|
{
|
|
string layerUVBaseParam = string.Format("{0}{1}", kUVBase, i);
|
|
UVBaseMapping layerUVBaseMapping = (UVBaseMapping)material.GetFloat(layerUVBaseParam);
|
|
string currentLayerMappingPlanar = string.Format("{0}{1}", kLayerMappingPlanar, i);
|
|
CoreUtils.SetKeyword(material, currentLayerMappingPlanar, layerUVBaseMapping == UVBaseMapping.Planar);
|
|
string currentLayerMappingTriplanar = string.Format("{0}{1}", kLayerMappingTriplanar, i);
|
|
CoreUtils.SetKeyword(material, currentLayerMappingTriplanar, layerUVBaseMapping == UVBaseMapping.Triplanar);
|
|
}
|
|
}
|
|
|
|
// All Setup Keyword functions must be static. It allow to create script to automatically update the shaders with a script if code change
|
|
static new public void SetupMaterialKeywordsAndPass(Material material)
|
|
{
|
|
SetupBaseLitKeywords(material);
|
|
SetupBaseLitMaterialPass(material);
|
|
|
|
// TODO: planar/triplannar supprt
|
|
//SetupLayersMappingKeywords(material);
|
|
|
|
bool enableHeightBlend = material.HasProperty(kEnableHeightBlend) && material.GetFloat(kEnableHeightBlend) > 0;
|
|
CoreUtils.SetKeyword(material, "_TERRAIN_BLEND_HEIGHT", enableHeightBlend);
|
|
|
|
bool enableInstancedPerPixelNormal = material.GetFloat(kEnableInstancedPerPixelNormal) > 0.0f;
|
|
CoreUtils.SetKeyword(material, "_TERRAIN_INSTANCED_PERPIXEL_NORMAL", enableInstancedPerPixelNormal);
|
|
}
|
|
|
|
public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] props)
|
|
{
|
|
FindBaseMaterialProperties(props);
|
|
FindMaterialProperties(props);
|
|
|
|
m_MaterialEditor = materialEditor;
|
|
// We should always do this call at the beginning
|
|
m_MaterialEditor.serializedObject.Update();
|
|
|
|
Material material = m_MaterialEditor.target as Material;
|
|
AssetImporter materialImporter = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(material.GetInstanceID()));
|
|
|
|
bool optionsChanged = false;
|
|
EditorGUI.BeginChangeCheck();
|
|
{
|
|
BaseMaterialPropertiesGUI();
|
|
if (enableHeightBlend != null)
|
|
{
|
|
EditorGUI.indentLevel++;
|
|
m_MaterialEditor.ShaderProperty(enableHeightBlend, styles.enableHeightBlend);
|
|
if (enableHeightBlend.floatValue > 0)
|
|
{
|
|
EditorGUI.indentLevel++;
|
|
m_MaterialEditor.ShaderProperty(heightTransition, styles.heightTransition);
|
|
EditorGUI.indentLevel--;
|
|
}
|
|
EditorGUI.indentLevel--;
|
|
}
|
|
EditorGUILayout.Space();
|
|
}
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
optionsChanged = true;
|
|
}
|
|
|
|
bool enablePerPixelNormalChanged = false;
|
|
|
|
EditorGUILayout.Space();
|
|
EditorGUILayout.LabelField(StylesBaseUnlit.advancedText, EditorStyles.boldLabel);
|
|
// NB RenderQueue editor is not shown on purpose: we want to override it based on blend mode
|
|
EditorGUI.indentLevel++;
|
|
m_MaterialEditor.EnableInstancingField();
|
|
if (m_MaterialEditor.IsInstancingEnabled())
|
|
{
|
|
EditorGUI.indentLevel++;
|
|
EditorGUI.BeginChangeCheck();
|
|
m_MaterialEditor.ShaderProperty(enableInstancedPerPixelNormal, styles.enableInstancedPerPixelNormal);
|
|
enablePerPixelNormalChanged = EditorGUI.EndChangeCheck();
|
|
EditorGUI.indentLevel--;
|
|
}
|
|
EditorGUI.indentLevel--;
|
|
|
|
if (optionsChanged || enablePerPixelNormalChanged)
|
|
{
|
|
foreach (var obj in m_MaterialEditor.targets)
|
|
{
|
|
SetupMaterialKeywordsAndPassInternal((Material)obj);
|
|
}
|
|
}
|
|
|
|
// We should always do this call at the end
|
|
m_MaterialEditor.serializedObject.ApplyModifiedProperties();
|
|
}
|
|
|
|
private bool m_ShowChannelRemapping = false;
|
|
|
|
enum HeightParametrization
|
|
{
|
|
Amplitude,
|
|
MinMax
|
|
};
|
|
private HeightParametrization m_HeightParametrization = HeightParametrization.Amplitude;
|
|
|
|
private static bool DoesTerrainUseMaskMaps(TerrainLayer[] terrainLayers)
|
|
{
|
|
for (int i = 0; i < terrainLayers.Length; ++i)
|
|
{
|
|
if (terrainLayers[i].maskMapTexture != null)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ITerrainLayerCustomUI.OnTerrainLayerGUI(TerrainLayer terrainLayer, Terrain terrain)
|
|
{
|
|
var terrainLayers = terrain.terrainData.terrainLayers;
|
|
if (!DoesTerrainUseMaskMaps(terrainLayers))
|
|
return false;
|
|
|
|
// Don't use the member field enableHeightBlend as ShaderGUI.OnGUI might not be called if the material UI is folded.
|
|
bool heightBlend = terrain.materialTemplate.HasProperty(kEnableHeightBlend) && terrain.materialTemplate.GetFloat(kEnableHeightBlend) > 0;
|
|
|
|
terrainLayer.diffuseTexture = EditorGUILayout.ObjectField(styles.diffuseTexture, terrainLayer.diffuseTexture, typeof(Texture2D), false) as Texture2D;
|
|
TerrainLayerUtility.ValidateDiffuseTextureUI(terrainLayer.diffuseTexture);
|
|
|
|
var diffuseRemapMin = terrainLayer.diffuseRemapMin;
|
|
var diffuseRemapMax = terrainLayer.diffuseRemapMax;
|
|
EditorGUI.BeginChangeCheck();
|
|
|
|
bool enableDensity = false;
|
|
if (terrainLayer.diffuseTexture != null)
|
|
{
|
|
var rect = GUILayoutUtility.GetLastRect();
|
|
rect.y += 16 + 4;
|
|
rect.width = EditorGUIUtility.labelWidth + 64;
|
|
rect.height = 16;
|
|
|
|
++EditorGUI.indentLevel;
|
|
|
|
var diffuseTint = new Color(diffuseRemapMax.x, diffuseRemapMax.y, diffuseRemapMax.z);
|
|
diffuseTint = EditorGUI.ColorField(rect, styles.colorTint, diffuseTint, true, false, false);
|
|
diffuseRemapMax.x = diffuseTint.r;
|
|
diffuseRemapMax.y = diffuseTint.g;
|
|
diffuseRemapMax.z = diffuseTint.b;
|
|
diffuseRemapMin.x = diffuseRemapMin.y = diffuseRemapMin.z = 0;
|
|
|
|
if (!heightBlend)
|
|
{
|
|
rect.y = rect.yMax + 2;
|
|
enableDensity = EditorGUI.Toggle(rect, styles.opacityAsDensity, diffuseRemapMin.w > 0);
|
|
}
|
|
|
|
--EditorGUI.indentLevel;
|
|
}
|
|
diffuseRemapMax.w = 1;
|
|
diffuseRemapMin.w = enableDensity ? 1 : 0;
|
|
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
terrainLayer.diffuseRemapMin = diffuseRemapMin;
|
|
terrainLayer.diffuseRemapMax = diffuseRemapMax;
|
|
}
|
|
|
|
terrainLayer.normalMapTexture = EditorGUILayout.ObjectField(styles.normalMapTexture, terrainLayer.normalMapTexture, typeof(Texture2D), false) as Texture2D;
|
|
TerrainLayerUtility.ValidateNormalMapTextureUI(terrainLayer.normalMapTexture, TerrainLayerUtility.CheckNormalMapTextureType(terrainLayer.normalMapTexture));
|
|
|
|
if (terrainLayer.normalMapTexture != null)
|
|
{
|
|
var rect = GUILayoutUtility.GetLastRect();
|
|
rect.y += 16 + 4;
|
|
rect.width = EditorGUIUtility.labelWidth + 64;
|
|
rect.height = 16;
|
|
|
|
++EditorGUI.indentLevel;
|
|
terrainLayer.normalScale = EditorGUI.FloatField(rect, styles.normalScale, terrainLayer.normalScale);
|
|
--EditorGUI.indentLevel;
|
|
}
|
|
|
|
terrainLayer.maskMapTexture = EditorGUILayout.ObjectField(heightBlend ? styles.maskMapTexture : styles.maskMapTextureWithoutHeight, terrainLayer.maskMapTexture, typeof(Texture2D), false) as Texture2D;
|
|
TerrainLayerUtility.ValidateMaskMapTextureUI(terrainLayer.maskMapTexture);
|
|
|
|
var maskMapRemapMin = terrainLayer.maskMapRemapMin;
|
|
var maskMapRemapMax = terrainLayer.maskMapRemapMax;
|
|
|
|
++EditorGUI.indentLevel;
|
|
EditorGUI.BeginChangeCheck();
|
|
|
|
m_ShowChannelRemapping = EditorGUILayout.Foldout(m_ShowChannelRemapping, terrainLayer.maskMapTexture != null ? s_Styles.channelRemapping : s_Styles.defaultValues);
|
|
if (m_ShowChannelRemapping)
|
|
{
|
|
if (terrainLayer.maskMapTexture != null)
|
|
{
|
|
float min, max;
|
|
min = maskMapRemapMin.x; max = maskMapRemapMax.x;
|
|
EditorGUILayout.MinMaxSlider(s_Styles.metallic, ref min, ref max, 0, 1);
|
|
maskMapRemapMin.x = min; maskMapRemapMax.x = max;
|
|
|
|
min = maskMapRemapMin.y; max = maskMapRemapMax.y;
|
|
EditorGUILayout.MinMaxSlider(s_Styles.ao, ref min, ref max, 0, 1);
|
|
maskMapRemapMin.y = min; maskMapRemapMax.y = max;
|
|
|
|
if (heightBlend)
|
|
{
|
|
EditorGUILayout.LabelField(styles.height);
|
|
++EditorGUI.indentLevel;
|
|
m_HeightParametrization = (HeightParametrization)EditorGUILayout.EnumPopup(styles.heightParametrization, m_HeightParametrization);
|
|
if (m_HeightParametrization == HeightParametrization.Amplitude)
|
|
{
|
|
// (height - heightBase) * amplitude
|
|
float amplitude = Mathf.Max(maskMapRemapMax.z - maskMapRemapMin.z, Mathf.Epsilon); // to avoid divide by zero
|
|
float heightBase = -maskMapRemapMin.z / amplitude;
|
|
amplitude = EditorGUILayout.FloatField(styles.heightAmplitude, amplitude * 100) / 100;
|
|
heightBase = EditorGUILayout.FloatField(styles.heightBase, heightBase);
|
|
maskMapRemapMin.z = -heightBase * amplitude;
|
|
maskMapRemapMax.z = (1 - heightBase) * amplitude;
|
|
}
|
|
else
|
|
{
|
|
maskMapRemapMin.z = EditorGUILayout.FloatField(styles.heightMin, maskMapRemapMin.z * 100) / 100;
|
|
maskMapRemapMax.z = EditorGUILayout.FloatField(styles.heightMax, maskMapRemapMax.z * 100) / 100;
|
|
}
|
|
--EditorGUI.indentLevel;
|
|
}
|
|
|
|
min = maskMapRemapMin.w; max = maskMapRemapMax.w;
|
|
EditorGUILayout.MinMaxSlider(s_Styles.smoothness, ref min, ref max, 0, 1);
|
|
maskMapRemapMin.w = min; maskMapRemapMax.w = max;
|
|
}
|
|
else
|
|
{
|
|
maskMapRemapMin.x = maskMapRemapMax.x = EditorGUILayout.Slider(s_Styles.metallic, maskMapRemapMin.x, 0, 1);
|
|
maskMapRemapMin.y = maskMapRemapMax.y = EditorGUILayout.Slider(s_Styles.ao, maskMapRemapMin.y, 0, 1);
|
|
if (heightBlend)
|
|
maskMapRemapMin.z = maskMapRemapMax.z = EditorGUILayout.FloatField(s_Styles.heightCm, maskMapRemapMin.z * 100) / 100;
|
|
maskMapRemapMin.w = maskMapRemapMax.w = EditorGUILayout.Slider(s_Styles.smoothness, maskMapRemapMin.w, 0, 1);
|
|
}
|
|
}
|
|
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
terrainLayer.maskMapRemapMin = maskMapRemapMin;
|
|
terrainLayer.maskMapRemapMax = maskMapRemapMax;
|
|
}
|
|
--EditorGUI.indentLevel;
|
|
|
|
EditorGUILayout.Space();
|
|
TerrainLayerUtility.TilingSettingsUI(terrainLayer);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
} // namespace UnityEditor
|