//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
//**************** Copyright (c) 2016 Marko Pintera (marko.pintera@gmail.com). All rights reserved. **********************//
using System.Collections.Generic;
using BansheeEngine;
namespace BansheeEditor
{
/** @addtogroup Inspectors
* @{
*/
///
/// Renders an inspector for the resource.
///
[CustomInspector(typeof (Material))]
internal class MaterialInspector : Inspector
{
private MaterialParamGUI[] guiParams;
private GUIResourceField shaderField;
private GUIEnumField builtinShaderField;
///
protected internal override void Initialize()
{
LoadResource();
Material material = InspectedObject as Material;
if (material == null)
return;
Shader activeShader = material.Shader.Value;
BuiltinShader builtinType = ShaderToBuiltin(activeShader);
builtinShaderField = new GUIEnumField(typeof(BuiltinShader), new LocEdString("Shader"));
builtinShaderField.Value = (ulong) builtinType;
builtinShaderField.OnSelectionChanged += x =>
{
BuiltinShader newBuiltinType = (BuiltinShader) x;
material.Shader = Builtin.GetShader(newBuiltinType);
EditorApplication.SetDirty(material);
RebuildParamGUI(material);
bool newIsCustom = newBuiltinType == BuiltinShader.Custom;
shaderField.Active = newIsCustom;
};
shaderField = new GUIResourceField(typeof(Shader), new LocEdString("Shader file"));
shaderField.ValueRef = material.Shader;
shaderField.OnChanged += (x) =>
{
Shader shader = Resources.Load(x.UUID);
material.Shader = shader;
EditorApplication.SetDirty(material);
RebuildParamGUI(material);
};
bool isCustom = builtinType == BuiltinShader.Custom;
shaderField.Active = isCustom;
RebuildParamGUI(material);
}
///
protected internal override InspectableState Refresh()
{
Material material = InspectedObject as Material;
if (material == null)
return InspectableState.NotModified;
if (material.Shader != shaderField.ValueRef)
{
shaderField.ValueRef = material.Shader;
RebuildParamGUI(material);
}
if (guiParams != null)
{
foreach (var param in guiParams)
param.Refresh(material);
}
return InspectableState.NotModified;
}
///
/// Recreates GUI elements for all material parameters.
///
/// Material to create parameters for
private void RebuildParamGUI(Material material)
{
if (guiParams != null)
{
foreach (var param in guiParams)
param.Destroy();
guiParams = null;
}
Layout.Clear();
Layout.AddElement(builtinShaderField);
Layout.AddElement(shaderField);
if (material != null && material.Shader != null)
guiParams = CreateMaterialGUI(material, Layout);
}
///
/// Converts a shader resource into a builtin shader.
///
/// Shader resource to convert.
/// Type of builtin shader, if any.
private BuiltinShader ShaderToBuiltin(Shader shader)
{
// Note: Need a better way to detect the builtin shader perhaps (store it in Material?)
Shader standardShader = Builtin.GetShader(BuiltinShader.Standard);
Shader transparentShader = Builtin.GetShader(BuiltinShader.Transparent);
; if(standardShader == shader)
return BuiltinShader.Standard;
else if(transparentShader == shader)
return BuiltinShader.Transparent;
return BuiltinShader.Custom;
}
///
/// Creates a set of objects in which each object represents a GUI for a material parameter.
///
/// Material for whose parameters to create GUI for.
/// Layout to add the parameter GUI elements to.
/// A material parameter GUI object for each supported material parameter.
static internal MaterialParamGUI[] CreateMaterialGUI(Material mat, GUILayout layout)
{
Shader shader = mat.Shader.Value;
if (shader == null)
return new MaterialParamGUI[0];
List guiParams = new List();
ShaderParameter[] shaderParams = shader.Parameters;
foreach (var param in shaderParams)
{
if (param.isInternal)
continue;
switch (param.type)
{
case ShaderParameterType.Float:
layout.AddSpace(5);
guiParams.Add(new MaterialParamFloatGUI(param, mat, layout));
break;
case ShaderParameterType.Vector2:
layout.AddSpace(5);
guiParams.Add(new MaterialParamVec2GUI(param, mat, layout));
break;
case ShaderParameterType.Vector3:
layout.AddSpace(5);
guiParams.Add(new MaterialParamVec3GUI(param, mat, layout));
break;
case ShaderParameterType.Vector4:
layout.AddSpace(5);
guiParams.Add(new MaterialParamVec4GUI(param, mat, layout));
break;
case ShaderParameterType.Matrix3:
layout.AddSpace(5);
guiParams.Add(new MaterialParamMat3GUI(param, mat, layout));
break;
case ShaderParameterType.Matrix4:
layout.AddSpace(5);
guiParams.Add(new MaterialParamMat4GUI(param, mat, layout));
break;
case ShaderParameterType.Color:
layout.AddSpace(5);
guiParams.Add(new MaterialParamColorGUI(param, mat, layout));
break;
case ShaderParameterType.Texture2D:
case ShaderParameterType.Texture3D:
case ShaderParameterType.TextureCube:
layout.AddSpace(5);
guiParams.Add(new MaterialParamTextureGUI(param, mat, layout));
break;
}
}
return guiParams.ToArray();
}
}
///
/// Contains GUI element(s) for a single parameter in a .
///
internal abstract class MaterialParamGUI
{
protected ShaderParameter shaderParam;
///
/// Creates a new material parameter GUI.
///
/// Shader parameter to create the GUI for.
protected MaterialParamGUI(ShaderParameter shaderParam)
{
this.shaderParam = shaderParam;
}
///
/// Checks if the data stored in GUI and in the material matches, and updates the GUI if it doesn't.
///
/// Material whose data to check.
internal abstract void Refresh(Material material);
///
/// Destroys the internal GUI elements.
///
internal abstract void Destroy();
}
///
/// Contains GUI element(s) for a single floating point parameter in a .
///
internal class MaterialParamFloatGUI : MaterialParamGUI
{
private GUIFloatField guiElem;
///
/// Creates a new material parameter GUI.
///
/// Shader parameter to create the GUI for. Must be of floating point type.
/// Material the parameter is a part of.
/// Layout to append the GUI elements to.
internal MaterialParamFloatGUI(ShaderParameter shaderParam, Material material, GUILayout layout)
: base(shaderParam)
{
LocString title = new LocEdString(shaderParam.name);
guiElem = new GUIFloatField(title);
guiElem.OnChanged += (x) =>
{
material.SetFloat(shaderParam.name, x);
EditorApplication.SetDirty(material);
};
layout.AddElement(guiElem);
}
///
internal override void Refresh(Material material)
{
guiElem.Value = material.GetFloat(shaderParam.name);
}
///
internal override void Destroy()
{
guiElem.Destroy();
}
}
///
/// Contains GUI element(s) for a single 2D vector parameter in a .
///
internal class MaterialParamVec2GUI : MaterialParamGUI
{
private GUIVector2Field guiElem;
///
/// Creates a new material parameter GUI.
///
/// Shader parameter to create the GUI for. Must be of 2D vector type.
/// Material the parameter is a part of.
/// Layout to append the GUI elements to.
internal MaterialParamVec2GUI(ShaderParameter shaderParam, Material material, GUILayout layout)
: base(shaderParam)
{
LocString title = new LocEdString(shaderParam.name);
guiElem = new GUIVector2Field(title);
guiElem.OnChanged += (x) =>
{
material.SetVector2(shaderParam.name, x);
EditorApplication.SetDirty(material);
};
layout.AddElement(guiElem);
}
///
internal override void Refresh(Material material)
{
guiElem.Value = material.GetVector2(shaderParam.name);
}
///
internal override void Destroy()
{
guiElem.Destroy();
}
}
///
/// Contains GUI element(s) for a single 3D vector parameter in a .
///
internal class MaterialParamVec3GUI : MaterialParamGUI
{
private GUIVector3Field guiElem;
///
/// Creates a new material parameter GUI.
///
/// Shader parameter to create the GUI for. Must be of 3D vector type.
/// Material the parameter is a part of.
/// Layout to append the GUI elements to.
internal MaterialParamVec3GUI(ShaderParameter shaderParam, Material material, GUILayout layout)
: base(shaderParam)
{
LocString title = new LocEdString(shaderParam.name);
guiElem = new GUIVector3Field(title);
guiElem.OnChanged += (x) =>
{
material.SetVector3(shaderParam.name, x);
EditorApplication.SetDirty(material);
};
layout.AddElement(guiElem);
}
///
internal override void Refresh(Material material)
{
guiElem.Value = material.GetVector3(shaderParam.name);
}
///
internal override void Destroy()
{
guiElem.Destroy();
}
}
///
/// Contains GUI element(s) for a single 4D vector parameter in a .
///
internal class MaterialParamVec4GUI : MaterialParamGUI
{
private GUIVector4Field guiElem;
///
/// Creates a new material parameter GUI.
///
/// Shader parameter to create the GUI for. Must be of 4D vector type.
/// Material the parameter is a part of.
/// Layout to append the GUI elements to.
internal MaterialParamVec4GUI(ShaderParameter shaderParam, Material material, GUILayout layout)
: base(shaderParam)
{
LocString title = new LocEdString(shaderParam.name);
guiElem = new GUIVector4Field(title);
guiElem.OnChanged += (x) =>
{
material.SetVector4(shaderParam.name, x);
EditorApplication.SetDirty(material);
};
layout.AddElement(guiElem);
}
///
internal override void Refresh(Material material)
{
guiElem.Value = material.GetVector4(shaderParam.name);
}
///
internal override void Destroy()
{
guiElem.Destroy();
}
}
///
/// Contains GUI element(s) for a single 3x3 matrix parameter in a .
///
internal class MaterialParamMat3GUI : MaterialParamGUI
{
private const int MAT_SIZE = 3;
private GUILayout mainLayout;
private GUIFloatField[] guiMatFields = new GUIFloatField[MAT_SIZE * MAT_SIZE];
///
/// Creates a new material parameter GUI.
///
/// Shader parameter to create the GUI for. Must be of 3x3 matrix type.
/// Material the parameter is a part of.
/// Layout to append the GUI elements to.
internal MaterialParamMat3GUI(ShaderParameter shaderParam, Material material, GUILayout layout)
: base(shaderParam)
{
LocString title = new LocEdString(shaderParam.name);
GUILabel guiTitle = new GUILabel(title, GUIOption.FixedWidth(100));
mainLayout = layout.AddLayoutY();
GUILayoutX titleLayout = mainLayout.AddLayoutX();
titleLayout.AddElement(guiTitle);
titleLayout.AddFlexibleSpace();
GUILayoutY contentLayout = mainLayout.AddLayoutY();
GUILayoutX[] rows = new GUILayoutX[MAT_SIZE];
for (int i = 0; i < rows.Length; i++)
rows[i] = contentLayout.AddLayoutX();
for (int row = 0; row < MAT_SIZE; row++)
{
for (int col = 0; col < MAT_SIZE; col++)
{
int index = row * MAT_SIZE + col;
guiMatFields[index] = new GUIFloatField(row + "," + col, 20, "", GUIOption.FixedWidth(80));
GUIFloatField field = guiMatFields[index];
rows[row].AddElement(field);
rows[row].AddSpace(5);
int hoistedRow = row;
int hoistedCol = col;
field.OnChanged += (x) =>
{
Matrix3 value = material.GetMatrix3(shaderParam.name);
value[hoistedRow, hoistedCol] = x;
material.SetMatrix3(shaderParam.name, value);
EditorApplication.SetDirty(material);
};
}
}
}
///
internal override void Refresh(Material material)
{
Matrix3 value = material.GetMatrix3(shaderParam.name);
for (int row = 0; row < MAT_SIZE; row++)
{
for (int col = 0; col < MAT_SIZE; col++)
{
int index = row * MAT_SIZE + col;
guiMatFields[index].Value = value[row, col];
}
}
}
///
internal override void Destroy()
{
mainLayout.Destroy();
}
}
///
/// Contains GUI element(s) for a single 4x4 matrix parameter in a .
///
internal class MaterialParamMat4GUI : MaterialParamGUI
{
private const int MAT_SIZE = 4;
private GUILayout mainLayout;
private GUIFloatField[] guiMatFields = new GUIFloatField[MAT_SIZE * MAT_SIZE];
///
/// Creates a new material parameter GUI.
///
/// Shader parameter to create the GUI for. Must be of 4x4 matrix type.
/// Material the parameter is a part of.
/// Layout to append the GUI elements to.
internal MaterialParamMat4GUI(ShaderParameter shaderParam, Material material, GUILayout layout)
: base(shaderParam)
{
LocString title = new LocEdString(shaderParam.name);
GUILabel guiTitle = new GUILabel(title, GUIOption.FixedWidth(100));
mainLayout = layout.AddLayoutY();
GUILayoutX titleLayout = mainLayout.AddLayoutX();
titleLayout.AddElement(guiTitle);
titleLayout.AddFlexibleSpace();
GUILayoutY contentLayout = mainLayout.AddLayoutY();
GUILayoutX[] rows = new GUILayoutX[MAT_SIZE];
for (int i = 0; i < rows.Length; i++)
rows[i] = contentLayout.AddLayoutX();
for (int row = 0; row < MAT_SIZE; row++)
{
for (int col = 0; col < MAT_SIZE; col++)
{
int index = row * MAT_SIZE + col;
guiMatFields[index] = new GUIFloatField(row + "," + col, 20, "", GUIOption.FixedWidth(80));
GUIFloatField field = guiMatFields[index];
rows[row].AddElement(field);
rows[row].AddSpace(5);
int hoistedRow = row;
int hoistedCol = col;
field.OnChanged += (x) =>
{
Matrix4 value = material.GetMatrix4(shaderParam.name);
value[hoistedRow, hoistedCol] = x;
material.SetMatrix4(shaderParam.name, value);
EditorApplication.SetDirty(material);
};
}
}
}
///
internal override void Refresh(Material material)
{
Matrix4 value = material.GetMatrix4(shaderParam.name);
for (int row = 0; row < MAT_SIZE; row++)
{
for (int col = 0; col < MAT_SIZE; col++)
{
int index = row * MAT_SIZE + col;
guiMatFields[index].Value = value[row, col];
}
}
}
///
internal override void Destroy()
{
mainLayout.Destroy();
}
}
///
/// Contains GUI element(s) for a single color parameter in a .
///
internal class MaterialParamColorGUI : MaterialParamGUI
{
private GUIColorField guiElem;
///
/// Creates a new material parameter GUI.
///
/// Shader parameter to create the GUI for. Must be of color type.
/// Material the parameter is a part of.
/// Layout to append the GUI elements to.
internal MaterialParamColorGUI(ShaderParameter shaderParam, Material material, GUILayout layout)
: base(shaderParam)
{
LocString title = new LocEdString(shaderParam.name);
guiElem = new GUIColorField(title);
guiElem.OnChanged += (x) =>
{
material.SetColor(shaderParam.name, x);
EditorApplication.SetDirty(material);
};
layout.AddElement(guiElem);
}
///
internal override void Refresh(Material material)
{
guiElem.Value = material.GetColor(shaderParam.name);
}
///
internal override void Destroy()
{
guiElem.Destroy();
}
}
///
/// Contains GUI element(s) for a single texture parameter in a .
///
internal class MaterialParamTextureGUI : MaterialParamGUI
{
private GUITextureField guiElem;
///
/// Creates a new material parameter GUI.
///
/// Shader parameter to create the GUI for. Must be of texture type.
/// Material the parameter is a part of.
/// Layout to append the GUI elements to.
internal MaterialParamTextureGUI(ShaderParameter shaderParam, Material material, GUILayout layout)
: base(shaderParam)
{
LocString title = new LocEdString(shaderParam.name);
guiElem = new GUITextureField(title);
switch (shaderParam.type)
{
case ShaderParameterType.Texture2D:
case ShaderParameterType.Texture3D:
case ShaderParameterType.TextureCube:
guiElem.OnChanged += (x) =>
{
Texture texture = Resources.Load(x.UUID);
material.SetTexture(shaderParam.name, texture);
EditorApplication.SetDirty(material);
};
break;
}
layout.AddElement(guiElem);
}
///
internal override void Refresh(Material material)
{
Texture value = null;
switch (shaderParam.type)
{
case ShaderParameterType.Texture2D:
case ShaderParameterType.Texture3D:
case ShaderParameterType.TextureCube:
value = material.GetTexture(shaderParam.name).Value;
break;
}
guiElem.Value = value;
}
///
internal override void Destroy()
{
guiElem.Destroy();
}
}
/** @} */
}