//********************************** 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(); } } /** @} */ }