634 lines
30 KiB
C#
634 lines
30 KiB
C#
|
using UnityEngine;
|
||
|
using UnityEditor;
|
||
|
using NUnit.Framework;
|
||
|
using System.Linq;
|
||
|
using System.Collections.Generic;
|
||
|
using System;
|
||
|
|
||
|
public class AvatarMaterialEditor : MaterialEditor {
|
||
|
|
||
|
static Dictionary<Material, int> layerVisibilityMasks = new Dictionary<Material, int>();
|
||
|
const int MaxLayerCount = 8;
|
||
|
|
||
|
private const string NormalMapPrefix = "NORMAL_MAP";
|
||
|
private const string ParallaxPrefix = "PARALLAX";
|
||
|
private const string RoughnessPrefix = "ROUGHNESS";
|
||
|
private const string LayerKeywordPrefix = "LAYERS_";
|
||
|
private const string AlphaMaskUniform = "_AlphaMask";
|
||
|
private const string DarkMultUniform = "_DarkMultiplier";
|
||
|
private const string BaseColorUniform = "_BaseColor";
|
||
|
private const string BaseMaskTypeUniform = "_BaseMaskType";
|
||
|
private const string BaseMaskParametersUniform = "_BaseMaskParameters";
|
||
|
private const string BaseMaskAxisUniform = "_BaseMaskAxis";
|
||
|
private const string NormalMapUniform = "_NormalMap";
|
||
|
private const string ParallaxMapUniform = "_ParallaxMap";
|
||
|
private const string RoughnessMapUniform = "_RoughnessMap";
|
||
|
private const string LayerSurfacePrefix = "_LayerSurface";
|
||
|
private const string LayerColorPrefix = "_LayerColor";
|
||
|
private const string LayerSampleParametersPrefix = "_LayerSampleParameters";
|
||
|
private const string LayerMaskTypePrefix = "_LayerMaskType";
|
||
|
private const string LayerMaskParametersPrefix = "_LayerMaskParameters";
|
||
|
private const string LayerMaskAxisPrefix = "_LayerMaskAxis";
|
||
|
private const string LayerBlendModePrefix = "_LayerBlendMode";
|
||
|
private const string LayerSampleModePrefix = "_LayerSampleMode";
|
||
|
|
||
|
enum LayerSampleMode : int
|
||
|
{
|
||
|
Color,
|
||
|
Texture,
|
||
|
TextureSingleChannel,
|
||
|
Parallax,
|
||
|
RSRM,
|
||
|
Count
|
||
|
}
|
||
|
static readonly string[] layerSampleModeLabels = new string[(int)LayerSampleMode.Count] { "Color", "Texture", "Texture (single channel)", "Parallax", "RSRM" };
|
||
|
|
||
|
enum LayerMaskType : int
|
||
|
{
|
||
|
None,
|
||
|
Positional,
|
||
|
ViewReflection,
|
||
|
Fresnel,
|
||
|
Pulse,
|
||
|
Count
|
||
|
}
|
||
|
static readonly string[] layerMaskTypeLabels = new string[(int)LayerMaskType.Count] { "None", "Positional", "View reflection", "Fresnel", "Pulse" };
|
||
|
|
||
|
enum LayerBlendMode : int
|
||
|
{
|
||
|
Add,
|
||
|
Multiply,
|
||
|
Count
|
||
|
}
|
||
|
static readonly string[] layerBlendModeLabels = new string[(int)LayerBlendMode.Count] { "Add", "Multiply" };
|
||
|
|
||
|
Material[] previewMaterials = new Material[MaxLayerCount];
|
||
|
Vector4[,] sampleParametersCache = new Vector4[MaxLayerCount, (int)LayerSampleMode.Count];
|
||
|
Vector4[][] maskParametersCache = new Vector4[MaxLayerCount][];
|
||
|
Vector4[][] maskAxisCache = new Vector4[MaxLayerCount][];
|
||
|
Vector4[] baseMaskParametersCache = new Vector4[(int)LayerMaskType.Count];
|
||
|
Vector4[] baseMaskAxisCache = new Vector4[(int)LayerMaskType.Count];
|
||
|
PreviewRenderUtility previewUtility;
|
||
|
Mesh previewMesh;
|
||
|
|
||
|
static readonly Vector4 PositionalMaskDefaults = new Vector4(0.0f, 1.0f, 1.0f, 0.0f);
|
||
|
static readonly Vector4 ViewReflectionMaskDefaults = new Vector4(1.0f, 0.0f, 1.0f, 0.0f);
|
||
|
static readonly Vector4 FresnelMaskDefaults = new Vector4(1.0f, 0.0f, 1.0f, 1.0f);
|
||
|
static readonly Vector4 PulseMaskDefaults = new Vector4(1.0f, 0.0f, 1.0f, 0.0f);
|
||
|
static readonly Vector4 MaskAxisDefault = new Vector4(0.0f, 1.0f, 0.0f, 0.0f);
|
||
|
|
||
|
void Init()
|
||
|
{
|
||
|
if (previewUtility == null)
|
||
|
{
|
||
|
previewUtility = new PreviewRenderUtility();
|
||
|
GameObject gameObject = (GameObject)EditorGUIUtility.LoadRequired("Previews/PreviewMaterials.fbx");
|
||
|
previewMesh = gameObject.transform.Find("sphere").GetComponent<MeshFilter>().sharedMesh;
|
||
|
}
|
||
|
|
||
|
baseMaskParametersCache[(int)LayerMaskType.Positional] = PositionalMaskDefaults;
|
||
|
baseMaskParametersCache[(int)LayerMaskType.ViewReflection] = ViewReflectionMaskDefaults;
|
||
|
baseMaskParametersCache[(int)LayerMaskType.Fresnel] = FresnelMaskDefaults;
|
||
|
baseMaskParametersCache[(int)LayerMaskType.Pulse] = PulseMaskDefaults;
|
||
|
|
||
|
baseMaskAxisCache[(int)LayerMaskType.Positional] = MaskAxisDefault;
|
||
|
baseMaskAxisCache[(int)LayerMaskType.ViewReflection] = MaskAxisDefault;
|
||
|
|
||
|
for (int i = 0; i < MaxLayerCount; ++i)
|
||
|
{
|
||
|
sampleParametersCache[i, (int)LayerSampleMode.Color] = new Vector4(1.0f, 1.0f, 1.0f, 1.0f);
|
||
|
sampleParametersCache[i, (int)LayerSampleMode.Texture] = new Vector4(0.0f, 0.0f, 1.0f, 1.0f);
|
||
|
sampleParametersCache[i, (int)LayerSampleMode.TextureSingleChannel] = new Vector4(1.0f, 0.0f, 0.0f, 0.0f);
|
||
|
sampleParametersCache[i, (int)LayerSampleMode.Parallax] = new Vector4(0.0f, 1.0f, 0.0f, 0.0f);
|
||
|
sampleParametersCache[i, (int)LayerSampleMode.RSRM] = new Vector4(0.0f, 1.0f, 1.0f, 0.0f);
|
||
|
|
||
|
Vector4[] parametersCache = new Vector4[(int)LayerMaskType.Count];
|
||
|
parametersCache[(int)LayerMaskType.Positional] = PositionalMaskDefaults;
|
||
|
parametersCache[(int)LayerMaskType.ViewReflection] = ViewReflectionMaskDefaults;
|
||
|
parametersCache[(int)LayerMaskType.Fresnel] = FresnelMaskDefaults;
|
||
|
parametersCache[(int)LayerMaskType.Pulse] = PulseMaskDefaults;
|
||
|
maskParametersCache[i] = parametersCache;
|
||
|
|
||
|
Vector4[] axisCache = new Vector4[(int)LayerMaskType.Count];
|
||
|
axisCache[(int)LayerMaskType.Positional] = MaskAxisDefault;
|
||
|
axisCache[(int)LayerMaskType.ViewReflection] = MaskAxisDefault;
|
||
|
axisCache[(int)LayerMaskType.Pulse] = MaskAxisDefault;
|
||
|
maskAxisCache[i] = axisCache;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static class AvatarMaterialEditorGUILayout
|
||
|
{
|
||
|
public static bool KeywordToggle(string label, Material material, string keywordPrefix)
|
||
|
{
|
||
|
bool isEnabled = material.IsKeywordEnabled(keywordPrefix + "_ON");
|
||
|
bool newIsEnabled = EditorGUILayout.Toggle(label, isEnabled);
|
||
|
if (newIsEnabled != isEnabled)
|
||
|
{
|
||
|
Undo.RecordObject(material, label + " change");
|
||
|
if (newIsEnabled)
|
||
|
{
|
||
|
material.EnableKeyword(keywordPrefix + "_ON");
|
||
|
material.DisableKeyword(keywordPrefix + "_OFF");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
material.EnableKeyword(keywordPrefix + "_OFF");
|
||
|
material.DisableKeyword(keywordPrefix + "_ON");
|
||
|
}
|
||
|
}
|
||
|
return newIsEnabled;
|
||
|
}
|
||
|
|
||
|
public static Color ColorField(string label, Material material, string propertyName)
|
||
|
{
|
||
|
Color color = material.GetColor(propertyName);
|
||
|
Color newColor = EditorGUILayout.ColorField(label, color);
|
||
|
if (newColor != color)
|
||
|
{
|
||
|
Undo.RecordObject(material, label + " change");
|
||
|
material.SetColor(propertyName, newColor);
|
||
|
}
|
||
|
return newColor;
|
||
|
}
|
||
|
|
||
|
internal static int IntField(string label, Material material, string propertyName, string[] valueNames)
|
||
|
{
|
||
|
int currentValue = material.GetInt(propertyName);
|
||
|
int newValue = EditorGUILayout.Popup(label, currentValue, valueNames);
|
||
|
if (newValue != currentValue)
|
||
|
{
|
||
|
Undo.RecordObject(material, label + " change");
|
||
|
material.SetInt(propertyName, newValue);
|
||
|
}
|
||
|
return newValue;
|
||
|
}
|
||
|
|
||
|
internal static Vector2 Vector2Field(string label, Material material, string propertyName)
|
||
|
{
|
||
|
Vector4 currentValue = material.GetVector(propertyName);
|
||
|
Vector2 currentVec2 = new Vector2(currentValue.x, currentValue.y);
|
||
|
Vector2 newVec2 = EditorGUILayout.Vector2Field(label, currentVec2);
|
||
|
if (newVec2 != currentVec2)
|
||
|
{
|
||
|
Undo.RecordObject(material, label + " change");
|
||
|
material.SetVector(propertyName, new Vector4(newVec2.x, newVec2.y, currentValue.z, currentValue.w));
|
||
|
}
|
||
|
return newVec2;
|
||
|
}
|
||
|
|
||
|
internal static Vector3 Vector3Field(string label, Material material, string propertyName)
|
||
|
{
|
||
|
Vector4 currentValue = material.GetVector(propertyName);
|
||
|
Vector3 currentVec3 = new Vector3(currentValue.x, currentValue.y, currentValue.z);
|
||
|
Vector3 newVec3 = EditorGUILayout.Vector3Field(label, currentVec3);
|
||
|
if (newVec3 != currentVec3)
|
||
|
{
|
||
|
Undo.RecordObject(material, label + " change");
|
||
|
material.SetVector(propertyName, new Vector4(newVec3.x, newVec3.y, newVec3.z, currentValue.w));
|
||
|
}
|
||
|
return newVec3;
|
||
|
}
|
||
|
|
||
|
internal static float VectorComponentField(string label, Material material, string propertyName, int componentIndex)
|
||
|
{
|
||
|
Vector4 currentValue = material.GetVector(propertyName);
|
||
|
float currentComponent = currentValue[componentIndex];
|
||
|
float newComponent = EditorGUILayout.FloatField(label, currentComponent);
|
||
|
if (newComponent != currentComponent)
|
||
|
{
|
||
|
Undo.RecordObject(material, label + " change");
|
||
|
currentValue[componentIndex] = newComponent;
|
||
|
material.SetVector(propertyName, currentValue);
|
||
|
}
|
||
|
return newComponent;
|
||
|
}
|
||
|
|
||
|
internal static Vector4 ChannelMaskField(string label, Material material, string propertyName)
|
||
|
{
|
||
|
Vector4 currentValue = material.GetVector(propertyName);
|
||
|
int currentChannel = 0;
|
||
|
for (int i = 0; i < 4; ++i)
|
||
|
{
|
||
|
if (currentValue[i] != 0)
|
||
|
{
|
||
|
currentChannel = i;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
int newChannel = EditorGUILayout.IntPopup(label, currentChannel, new string[] { "R", "G", "B", "A" }, new int[] { 0, 1, 2, 3 });
|
||
|
if (newChannel != currentChannel)
|
||
|
{
|
||
|
Vector4 channelMask = Vector4.zero;
|
||
|
channelMask[newChannel] = 1.0f;
|
||
|
Undo.RecordObject(material, label + " change");
|
||
|
currentValue = channelMask;
|
||
|
material.SetVector(propertyName, currentValue);
|
||
|
}
|
||
|
return currentValue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void MaskField(
|
||
|
string label,
|
||
|
Material material,
|
||
|
string maskTypePropertyName,
|
||
|
string maskParametersPropertyName,
|
||
|
string maskAxisPropertyName,
|
||
|
Vector4[] axisCache,
|
||
|
Vector4[] parameterCache,
|
||
|
bool normalMapEnabled)
|
||
|
{
|
||
|
EditorGUI.BeginChangeCheck();
|
||
|
LayerMaskType maskType = (LayerMaskType)AvatarMaterialEditorGUILayout.IntField(label, material, maskTypePropertyName, layerMaskTypeLabels);
|
||
|
if (EditorGUI.EndChangeCheck())
|
||
|
{
|
||
|
material.SetVector(maskAxisPropertyName, axisCache[(int)maskType]);
|
||
|
material.SetVector(maskParametersPropertyName, parameterCache[(int)maskType]);
|
||
|
}
|
||
|
|
||
|
// Show mask-specific controls
|
||
|
EditorGUI.BeginChangeCheck();
|
||
|
switch (maskType)
|
||
|
{
|
||
|
case LayerMaskType.Positional:
|
||
|
AvatarMaterialEditorGUILayout.Vector3Field("Axis", material, maskAxisPropertyName);
|
||
|
AvatarMaterialEditorGUILayout.VectorComponentField("Center distance", material, maskParametersPropertyName, 0);
|
||
|
AvatarMaterialEditorGUILayout.VectorComponentField("Fade above", material, maskParametersPropertyName, 1);
|
||
|
AvatarMaterialEditorGUILayout.VectorComponentField("Fade below", material, maskParametersPropertyName, 2);
|
||
|
break;
|
||
|
case LayerMaskType.ViewReflection:
|
||
|
AvatarMaterialEditorGUILayout.Vector3Field("Axis", material, maskAxisPropertyName);
|
||
|
AvatarMaterialEditorGUILayout.VectorComponentField("Fade begin", material, maskParametersPropertyName, 0);
|
||
|
AvatarMaterialEditorGUILayout.VectorComponentField("Fade end", material, maskParametersPropertyName, 1);
|
||
|
if (normalMapEnabled)
|
||
|
{
|
||
|
AvatarMaterialEditorGUILayout.VectorComponentField("Normal map strength", material, maskParametersPropertyName, 2);
|
||
|
}
|
||
|
break;
|
||
|
case LayerMaskType.Fresnel:
|
||
|
AvatarMaterialEditorGUILayout.VectorComponentField("Power", material, maskParametersPropertyName, 0);
|
||
|
AvatarMaterialEditorGUILayout.VectorComponentField("Fade begin", material, maskParametersPropertyName, 1);
|
||
|
AvatarMaterialEditorGUILayout.VectorComponentField("Fade end", material, maskParametersPropertyName, 2);
|
||
|
if (normalMapEnabled)
|
||
|
{
|
||
|
AvatarMaterialEditorGUILayout.VectorComponentField("Normal map strength", material, maskParametersPropertyName, 3);
|
||
|
}
|
||
|
break;
|
||
|
case LayerMaskType.Pulse:
|
||
|
AvatarMaterialEditorGUILayout.Vector3Field("Axis", material, maskAxisPropertyName);
|
||
|
AvatarMaterialEditorGUILayout.VectorComponentField("Pulse distance", material, maskParametersPropertyName, 0);
|
||
|
AvatarMaterialEditorGUILayout.VectorComponentField("Pulse speed", material, maskParametersPropertyName, 1);
|
||
|
AvatarMaterialEditorGUILayout.VectorComponentField("Power", material, maskParametersPropertyName, 2);
|
||
|
break;
|
||
|
}
|
||
|
if (EditorGUI.EndChangeCheck())
|
||
|
{
|
||
|
parameterCache[(int)maskType] = material.GetVector(maskParametersPropertyName);
|
||
|
axisCache[(int)maskType] = material.GetVector(maskAxisPropertyName);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void OnInspectorGUI()
|
||
|
{
|
||
|
Init();
|
||
|
if (!isVisible)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
SetDefaultGUIWidths();
|
||
|
|
||
|
Material material = target as Material;
|
||
|
int layerCount = MaxLayerCount;
|
||
|
while (layerCount > 0 && !material.IsKeywordEnabled(GetPropertyName(LayerKeywordPrefix, layerCount)))
|
||
|
{
|
||
|
--layerCount;
|
||
|
}
|
||
|
if (layerCount == 0)
|
||
|
{
|
||
|
layerCount = 1;
|
||
|
material.EnableKeyword("LAYERS_" + layerCount);
|
||
|
}
|
||
|
|
||
|
EditorGUILayout.LabelField("Global material properties");
|
||
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||
|
TextureField("AlphaMask", material, AlphaMaskUniform);
|
||
|
AvatarMaterialEditorGUILayout.ColorField("DarkMultiplier", material, DarkMultUniform);
|
||
|
AvatarMaterialEditorGUILayout.ColorField("BaseColor", material, BaseColorUniform);
|
||
|
bool normalMapEnabled = AvatarMaterialEditorGUILayout.KeywordToggle("Normal map enabled", material, NormalMapPrefix);
|
||
|
if (normalMapEnabled)
|
||
|
{
|
||
|
TextureField("Normal map", material, NormalMapUniform);
|
||
|
}
|
||
|
bool parallaxEnabled = AvatarMaterialEditorGUILayout.KeywordToggle("Parallax enabled", material, ParallaxPrefix);
|
||
|
if (parallaxEnabled)
|
||
|
{
|
||
|
TextureField("Parallax map", material, ParallaxMapUniform);
|
||
|
}
|
||
|
bool roughnessEnabled = AvatarMaterialEditorGUILayout.KeywordToggle("Roughness enabled", material, RoughnessPrefix);
|
||
|
if (roughnessEnabled)
|
||
|
{
|
||
|
TextureField("Roughness map", material, RoughnessMapUniform);
|
||
|
}
|
||
|
|
||
|
MaskField("Base mask type", material, BaseMaskTypeUniform, BaseMaskParametersUniform, BaseMaskAxisUniform, baseMaskAxisCache, baseMaskParametersCache, normalMapEnabled);
|
||
|
|
||
|
EditorGUILayout.EndVertical();
|
||
|
|
||
|
int layerVisibilityMask = 0;
|
||
|
layerVisibilityMasks.TryGetValue(material, out layerVisibilityMask);
|
||
|
|
||
|
EditorGUILayout.LabelField("Layers");
|
||
|
EditorGUI.indentLevel++;
|
||
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||
|
int? removeLayerIndex = null;
|
||
|
int? swapSource = null;
|
||
|
int? swapTarget = null;
|
||
|
for (int i = 0; i < layerCount; ++i)
|
||
|
{
|
||
|
|
||
|
// Draw the preview material
|
||
|
if (previewMaterials[i] == null)
|
||
|
previewMaterials[i] = CreatePreviewMaterial(material, i);
|
||
|
|
||
|
const int previewSize = 64;
|
||
|
const int buttonSize = 20;
|
||
|
Rect layerHeaderRect = GUILayoutUtility.GetRect(previewSize, previewSize, GUILayout.ExpandWidth(true));
|
||
|
|
||
|
// Draw the preview texture
|
||
|
#if UNITY_2017_1_OR_NEWER
|
||
|
Camera cam = previewUtility.camera;
|
||
|
#else
|
||
|
Camera cam = previewUtility.m_Camera;
|
||
|
#endif
|
||
|
cam.transform.position = Vector3.forward * 5.0f;
|
||
|
cam.transform.rotation = Quaternion.identity;
|
||
|
cam.transform.LookAt(Vector3.zero);
|
||
|
previewUtility.BeginStaticPreview(new Rect(0, 0, previewSize, previewSize));
|
||
|
previewUtility.DrawMesh(previewMesh, Vector3.zero, Quaternion.identity, previewMaterials[i], 0);
|
||
|
cam.Render();
|
||
|
Texture preview = previewUtility.EndStaticPreview();
|
||
|
GUI.Label(new Rect(layerHeaderRect.xMax - previewSize - buttonSize, layerHeaderRect.y, previewSize, previewSize), preview);
|
||
|
|
||
|
float yButton = layerHeaderRect.y;
|
||
|
EditorGUI.BeginDisabledGroup(layerCount <= 1);
|
||
|
if (GUI.Button(new Rect(layerHeaderRect.xMax - buttonSize, yButton, buttonSize, buttonSize), new GUIContent("X", "Remove layer")))
|
||
|
{
|
||
|
removeLayerIndex = i;
|
||
|
}
|
||
|
yButton += buttonSize + 4;
|
||
|
EditorGUI.EndDisabledGroup();
|
||
|
|
||
|
EditorGUI.BeginDisabledGroup(i == 0);
|
||
|
if (GUI.Button(new Rect(layerHeaderRect.xMax - buttonSize, yButton, buttonSize, buttonSize), new GUIContent("^", "Move layer up")))
|
||
|
{
|
||
|
swapSource = i;
|
||
|
swapTarget = i - 1;
|
||
|
}
|
||
|
yButton += buttonSize;
|
||
|
EditorGUI.EndDisabledGroup();
|
||
|
|
||
|
EditorGUI.BeginDisabledGroup(i == layerCount - 1);
|
||
|
if (GUI.Button(new Rect(layerHeaderRect.xMax - buttonSize, yButton, buttonSize, buttonSize), new GUIContent("v", "Move layer down")))
|
||
|
{
|
||
|
swapSource = i;
|
||
|
swapTarget = i + 1;
|
||
|
}
|
||
|
yButton += buttonSize;
|
||
|
EditorGUI.EndDisabledGroup();
|
||
|
|
||
|
// Create a toggleable group for the layer
|
||
|
int layerMaskBit = 1 << i;
|
||
|
bool layerVisible = (layerVisibilityMask & layerMaskBit) != 0;
|
||
|
layerVisible = EditorGUI.Foldout(layerHeaderRect, layerVisible, string.Format("Layer {0}", i + 1));
|
||
|
|
||
|
if (layerVisible)
|
||
|
layerVisibilityMask |= layerMaskBit;
|
||
|
else
|
||
|
layerVisibilityMask &= ~layerMaskBit;
|
||
|
if (layerVisible)
|
||
|
{
|
||
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||
|
EditorGUI.BeginChangeCheck();
|
||
|
{
|
||
|
// Handle the blend mode
|
||
|
AvatarMaterialEditorGUILayout.IntField("Blend mode", material, GetPropertyName(LayerBlendModePrefix, i), layerBlendModeLabels);
|
||
|
|
||
|
// Handle the sample mode selector
|
||
|
string layerSampleParametersProperty = GetPropertyName(LayerSampleParametersPrefix, i);
|
||
|
EditorGUI.BeginChangeCheck();
|
||
|
LayerSampleMode sampleMode = (LayerSampleMode)AvatarMaterialEditorGUILayout.IntField("Sample mode", material, GetPropertyName(LayerSampleModePrefix, i), layerSampleModeLabels);
|
||
|
if (EditorGUI.EndChangeCheck())
|
||
|
{
|
||
|
material.SetVector(layerSampleParametersProperty, sampleParametersCache[i, (int)sampleMode]);
|
||
|
}
|
||
|
|
||
|
// Show the mode-specific sample controls
|
||
|
EditorGUI.BeginChangeCheck();
|
||
|
AvatarMaterialEditorGUILayout.ColorField("Surface color", material, GetPropertyName(LayerColorPrefix, i));
|
||
|
switch (sampleMode) {
|
||
|
case LayerSampleMode.Texture:
|
||
|
TextureField("Surface texture", material, GetPropertyName(LayerSurfacePrefix, i));
|
||
|
AvatarMaterialEditorGUILayout.Vector2Field("Panning speed", material, layerSampleParametersProperty);
|
||
|
break;
|
||
|
case LayerSampleMode.TextureSingleChannel:
|
||
|
TextureField("Surface texture", material, GetPropertyName(LayerSurfacePrefix, i));
|
||
|
AvatarMaterialEditorGUILayout.ChannelMaskField("Channel", material, layerSampleParametersProperty);
|
||
|
break;
|
||
|
case LayerSampleMode.Parallax:
|
||
|
TextureField("Surface texture", material, GetPropertyName(LayerSurfacePrefix, i));
|
||
|
AvatarMaterialEditorGUILayout.VectorComponentField("Parallax min height", material, layerSampleParametersProperty, 0);
|
||
|
AvatarMaterialEditorGUILayout.VectorComponentField("Parallax max height", material, layerSampleParametersProperty, 1);
|
||
|
break;
|
||
|
case LayerSampleMode.RSRM:
|
||
|
TextureField("RSRM texture", material, GetPropertyName(LayerSurfacePrefix, i));
|
||
|
if (roughnessEnabled)
|
||
|
{
|
||
|
AvatarMaterialEditorGUILayout.VectorComponentField("Roughness min", material, layerSampleParametersProperty, 0);
|
||
|
AvatarMaterialEditorGUILayout.VectorComponentField("Roughness max", material, layerSampleParametersProperty, 1);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
AvatarMaterialEditorGUILayout.VectorComponentField("Roughness", material, layerSampleParametersProperty, 0);
|
||
|
}
|
||
|
if (normalMapEnabled)
|
||
|
{
|
||
|
AvatarMaterialEditorGUILayout.VectorComponentField("Normal map strength", material, layerSampleParametersProperty, 2);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
if (EditorGUI.EndChangeCheck())
|
||
|
{
|
||
|
sampleParametersCache[i, (int)sampleMode] = material.GetVector(layerSampleParametersProperty);
|
||
|
}
|
||
|
|
||
|
// Handle the mask mode selector
|
||
|
string maskParametersName = GetPropertyName(LayerMaskParametersPrefix, i);
|
||
|
string maskAxisName = GetPropertyName(LayerMaskAxisPrefix, i);
|
||
|
MaskField("Mask Type", material, GetPropertyName(LayerMaskTypePrefix, i), maskParametersName, maskAxisName, maskAxisCache[i], maskParametersCache[i], normalMapEnabled);
|
||
|
}
|
||
|
if (EditorGUI.EndChangeCheck())
|
||
|
{
|
||
|
previewMaterials[i] = null;
|
||
|
}
|
||
|
EditorGUILayout.EndVertical();
|
||
|
}
|
||
|
}
|
||
|
layerVisibilityMasks[material] = layerVisibilityMask;
|
||
|
|
||
|
if (layerCount < MaxLayerCount)
|
||
|
{
|
||
|
if (GUILayout.Button("Add layer"))
|
||
|
{
|
||
|
Undo.RecordObject(material, "Add layer");
|
||
|
SetLayerCount(material, layerCount + 1);
|
||
|
removeLayerIndex = null;
|
||
|
}
|
||
|
}
|
||
|
if (removeLayerIndex.HasValue)
|
||
|
{
|
||
|
Undo.RecordObject(material, "Remove layer");
|
||
|
for (int i = removeLayerIndex.Value; i < layerCount - 1; ++i)
|
||
|
{
|
||
|
CopyAttributes(material, i + 1, i);
|
||
|
}
|
||
|
SetLayerCount(material, layerCount - 1);
|
||
|
}
|
||
|
if (swapSource.HasValue && swapTarget.HasValue)
|
||
|
{
|
||
|
Undo.RecordObject(material, string.Format("Swap layers {1} and {0}", swapSource.Value, swapTarget.Value));
|
||
|
SwapAttributes(material, swapSource.Value, swapTarget.Value);
|
||
|
}
|
||
|
|
||
|
EditorGUI.indentLevel--;
|
||
|
EditorGUILayout.EndVertical();
|
||
|
EditorUtility.SetDirty(target);
|
||
|
}
|
||
|
|
||
|
public Texture TextureField(string label, Material material, string propertyName)
|
||
|
{
|
||
|
Texture texture = material.GetTexture(propertyName);
|
||
|
MaterialProperty property = MaterialEditor.GetMaterialProperty(new[] { material }, propertyName);
|
||
|
Texture newTexture = base.TextureProperty(property, label);
|
||
|
if (newTexture != texture)
|
||
|
{
|
||
|
Undo.RecordObject(material, label + " change");
|
||
|
material.SetTexture(propertyName, newTexture);
|
||
|
}
|
||
|
return newTexture;
|
||
|
}
|
||
|
|
||
|
private Material CreatePreviewMaterial(Material material, int layerIndex)
|
||
|
{
|
||
|
Material previewMaterial = new Material(material);
|
||
|
CopyAttributes(previewMaterial, layerIndex, 0);
|
||
|
SetLayerCount(previewMaterial, 1);
|
||
|
previewMaterial.SetVector(DarkMultUniform, new Vector4(0.6f, 0.6f, 0.6f, 1.0f));
|
||
|
previewMaterial.SetVector(BaseColorUniform, new Vector4(0.0f, 0.0f, 0.0f, 1.0f));
|
||
|
previewMaterial.SetTexture(AlphaMaskUniform, EditorGUIUtility.whiteTexture);
|
||
|
return previewMaterial;
|
||
|
}
|
||
|
|
||
|
private void SetLayerCount(Material material, int layerCount)
|
||
|
{
|
||
|
for (int i = 0; i < MaxLayerCount; ++i)
|
||
|
{
|
||
|
string layerCountProperty = GetPropertyName("LAYERS_", i + 1);
|
||
|
if (i + 1 == layerCount)
|
||
|
material.EnableKeyword(layerCountProperty);
|
||
|
else
|
||
|
material.DisableKeyword(layerCountProperty);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static string GetPropertyName(string baseName, int index)
|
||
|
{
|
||
|
return string.Format("{0}{1}", baseName, index);
|
||
|
}
|
||
|
|
||
|
class LayerAttributes
|
||
|
{
|
||
|
public Texture surface { get; private set; }
|
||
|
public Color color { get; private set; }
|
||
|
public LayerMaskType maskType { get; private set; }
|
||
|
public Vector4 maskParameters { get; private set; }
|
||
|
public Vector4 maskAxis { get; private set; }
|
||
|
public LayerSampleMode sampleMode { get; private set; }
|
||
|
public Vector4 sampleParameters { get; private set; }
|
||
|
public LayerBlendMode blendMode { get; private set; }
|
||
|
public LayerAttributes()
|
||
|
{
|
||
|
this.surface = null;
|
||
|
this.color = Color.white;
|
||
|
this.maskType = (int)LayerMaskType.None;
|
||
|
this.maskParameters = Vector4.zero;
|
||
|
this.maskAxis = new Vector4(0.0f, 1.0f, 0.0f, 0.0f);
|
||
|
this.sampleMode = (int)LayerSampleMode.Color;
|
||
|
this.sampleParameters = new Vector4(1.0f, 1.0f, 1.0f, 1.0f);
|
||
|
this.blendMode = (int)LayerBlendMode.Add;
|
||
|
}
|
||
|
public LayerAttributes(
|
||
|
Texture surface,
|
||
|
Color color,
|
||
|
LayerMaskType maskType,
|
||
|
Vector3 maskParameters,
|
||
|
Vector4 maskAxis,
|
||
|
LayerSampleMode sampleMode,
|
||
|
Vector4 sampleParameters,
|
||
|
LayerBlendMode blendMode
|
||
|
) {
|
||
|
this.surface = surface;
|
||
|
this.color = color;
|
||
|
this.maskType = maskType;
|
||
|
this.maskParameters = maskParameters;
|
||
|
this.maskAxis = maskAxis;
|
||
|
this.sampleMode = sampleMode;
|
||
|
this.sampleParameters = sampleParameters;
|
||
|
this.blendMode = blendMode;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void CopyAttributes(Material material, int sourceIndex, int targetIndex)
|
||
|
{
|
||
|
LayerAttributes attributes = GetLayerAttributes(material, sourceIndex);
|
||
|
SetLayerAttributes(material, targetIndex, attributes);
|
||
|
previewMaterials[targetIndex] = null;
|
||
|
}
|
||
|
|
||
|
private void SwapAttributes(Material material, int sourceIndex, int targetIndex)
|
||
|
{
|
||
|
LayerAttributes sourceAttributes = GetLayerAttributes(material, sourceIndex);
|
||
|
LayerAttributes targetAttributes = GetLayerAttributes(material, targetIndex);
|
||
|
SetLayerAttributes(material, sourceIndex, targetAttributes);
|
||
|
SetLayerAttributes(material, targetIndex, sourceAttributes);
|
||
|
previewMaterials[sourceIndex] = null;
|
||
|
previewMaterials[targetIndex] = null;
|
||
|
}
|
||
|
|
||
|
private static LayerAttributes GetLayerAttributes(Material material, int layerIndex)
|
||
|
{
|
||
|
return new LayerAttributes(
|
||
|
material.GetTexture(GetPropertyName(LayerSurfacePrefix, layerIndex)),
|
||
|
material.GetColor(GetPropertyName(LayerColorPrefix, layerIndex)),
|
||
|
(LayerMaskType)material.GetInt(GetPropertyName(LayerMaskTypePrefix, layerIndex)),
|
||
|
material.GetVector(GetPropertyName(LayerMaskParametersPrefix, layerIndex)),
|
||
|
material.GetVector(GetPropertyName(LayerMaskAxisPrefix, layerIndex)),
|
||
|
(LayerSampleMode)material.GetInt(GetPropertyName(LayerSampleModePrefix, layerIndex)),
|
||
|
material.GetVector(GetPropertyName(LayerSampleParametersPrefix, layerIndex)),
|
||
|
(LayerBlendMode)material.GetInt(GetPropertyName(LayerBlendModePrefix, layerIndex))
|
||
|
);
|
||
|
}
|
||
|
|
||
|
private static void SetLayerAttributes(Material material, int layerIndex, LayerAttributes attributes)
|
||
|
{
|
||
|
material.SetTexture(GetPropertyName(LayerSurfacePrefix, layerIndex), attributes.surface);
|
||
|
material.SetColor(GetPropertyName(LayerColorPrefix, layerIndex), attributes.color);
|
||
|
material.SetInt(GetPropertyName(LayerMaskTypePrefix, layerIndex), (int)attributes.maskType);
|
||
|
material.SetVector(GetPropertyName(LayerMaskParametersPrefix, layerIndex), attributes.maskParameters);
|
||
|
material.SetVector(GetPropertyName(LayerMaskAxisPrefix, layerIndex), attributes.maskAxis);
|
||
|
material.SetInt(GetPropertyName(LayerSampleModePrefix, layerIndex), (int)attributes.sampleMode);
|
||
|
material.SetVector(GetPropertyName(LayerSampleParametersPrefix, layerIndex), attributes.sampleParameters);
|
||
|
material.SetInt(GetPropertyName(LayerBlendModePrefix, layerIndex), (int)attributes.blendMode);
|
||
|
}
|
||
|
}
|