using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using UnityEngine; namespace UnityEditor.Graphs.Material { public abstract class PropertyChunk { protected string m_PropertyName; protected string m_PropertyDescription; protected bool m_Hidden; protected PropertyChunk(string propertyName, string propertyDescription, bool hidden) { m_PropertyName = propertyName; m_PropertyDescription = propertyDescription; m_Hidden = hidden; } public abstract string GetPropertyString(); public string propertyName { get { return m_PropertyName; } } public string propertyDescription { get { return m_PropertyDescription; } } } public class TexturePropertyChunk : PropertyChunk { private readonly Texture m_DefaultTexture; private readonly TextureType m_DefaultTextureType; public TexturePropertyChunk(string propertyName, string propertyDescription, Texture defaultTexture, TextureType defaultTextureType, bool hidden) : base(propertyName, propertyDescription, hidden) { m_DefaultTexture = defaultTexture; m_DefaultTextureType = defaultTextureType; } public override string GetPropertyString() { var result = new StringBuilder(); if (m_Hidden) result.Append ("[HideInInspector] "); result.Append(m_PropertyName); result.Append("(\""); result.Append(m_PropertyDescription); result.Append("\", 2D) = \""); result.Append(Enum.GetName(typeof(TextureType), m_DefaultTextureType).ToLower()); result.Append("\" {}"); return result.ToString(); } public Texture defaultTexture { get { return m_DefaultTexture; } } } public class ColorPropertyChunk : PropertyChunk { private Color m_DefaultColor; public ColorPropertyChunk(string propertyName, string propertyDescription, Color defaultColor, bool hidden) : base(propertyName, propertyDescription, hidden) { m_DefaultColor = defaultColor; } public override string GetPropertyString() { var result = new StringBuilder(); result.Append(m_PropertyName); result.Append("(\""); result.Append(m_PropertyDescription); result.Append("\", Color) = ("); result.Append(m_DefaultColor.r); result.Append(","); result.Append(m_DefaultColor.g); result.Append(","); result.Append(m_DefaultColor.b); result.Append(","); result.Append(m_DefaultColor.a); result.Append(")"); return result.ToString(); } } public class VectorPropertyChunk : PropertyChunk { private Vector4 m_DefaultVector; public VectorPropertyChunk(string propertyName, string propertyDescription, Vector4 defaultVector, bool hidden) : base(propertyName, propertyDescription, hidden) { m_DefaultVector = defaultVector; } public override string GetPropertyString() { var result = new StringBuilder(); result.Append(m_PropertyName); result.Append("(\""); result.Append(m_PropertyDescription); result.Append("\", Vector) = ("); result.Append(m_DefaultVector.x); result.Append(","); result.Append(m_DefaultVector.y); result.Append(","); result.Append(m_DefaultVector.z); result.Append(","); result.Append(m_DefaultVector.w); result.Append(")"); return result.ToString(); } } public class PropertyGenerator { private readonly List m_Properties = new List(); public void AddShaderProperty(PropertyChunk chunk) { m_Properties.Add(chunk); } public string GetShaderString(int baseIndentLevel) { var sb = new StringBuilder(); foreach (var prop in m_Properties) { for (var i = 0; i < baseIndentLevel; i++) { sb.Append("\t"); } sb.Append(prop.GetPropertyString()); sb.Append("\n"); } return sb.ToString(); } public Dictionary GetDefaultTexutres() { var result = new Dictionary(); foreach (var prop in m_Properties.OfType()) { if (prop.propertyName != null) result.Add(prop.propertyName, prop.defaultTexture); } return result; } } public class ShaderGenerator { private struct ShaderChunk { public ShaderChunk(int indentLevel, string shaderChunkString) { m_IndentLevel = indentLevel; m_ShaderChunkString = shaderChunkString; } private readonly int m_IndentLevel; private readonly string m_ShaderChunkString; public int chunkIndentLevel { get { return m_IndentLevel; } } public string chunkString { get { return m_ShaderChunkString; } } } private readonly List m_ShaderChunks = new List(); private int m_IndentLevel; private string m_Pragma; public void AddPragmaChunk(string s) { m_Pragma += s; } public string GetPragmaString() { return m_Pragma; } public void AddShaderChunk(string s, bool unique) { if (unique && m_ShaderChunks.Any(x => x.chunkString == s)) return; m_ShaderChunks.Add(new ShaderChunk(m_IndentLevel, s)); } public void Indent() { m_IndentLevel++; } public void Deindent() { m_IndentLevel = Math.Max(0, m_IndentLevel - 1); } public string GetShaderString(int baseIndentLevel) { var sb = new StringBuilder(); foreach (var shaderChunk in m_ShaderChunks) { var replaceString = "\n"; for (var i = 0; i < shaderChunk.chunkIndentLevel + baseIndentLevel; i++) { sb.Append("\t"); replaceString += "\t"; } sb.AppendLine(shaderChunk.chunkString.Replace("\n", replaceString)); } return sb.ToString(); } public static string GeneratePreviewShader(BaseMaterialNode node, out PreviewMode generatedShaderMode) { // figure out what kind of preview we want! var childNodes = node.CollectChildNodesByExecutionOrder(); var generationMode = GenerationMode.Preview2D; generatedShaderMode = PreviewMode.Preview2D; if (childNodes.Any(x => x.previewMode == PreviewMode.Preview3D)) { generationMode = GenerationMode.Preview3D; generatedShaderMode = PreviewMode.Preview3D; } string templateLocation = Path.Combine (Application.dataPath, Path.Combine ("MaterialGraph", generationMode == GenerationMode.Preview2D ? "2DPreview.template" : "3DPreview.template")); if (!File.Exists (templateLocation)) return null; string template = File.ReadAllText (templateLocation); var shaderBodyVisitor = new ShaderGenerator (); var shaderInputVisitor = new ShaderGenerator (); var shaderFunctionVisitor = new ShaderGenerator (); var shaderPropertiesVisitor = new PropertyGenerator (); var shaderPropertyUsagesVisitor = new ShaderGenerator (); var vertexShaderBlock = new ShaderGenerator (); var shaderName = "Hidden/PreviewShader/" + node.GetOutputVariableNameForSlot(node.outputSlots.First(), generationMode); var activeNodeList = node.CollectChildNodesByExecutionOrder (); if (node.graph is IGenerateGraphProperties) (node.graph as IGenerateGraphProperties).GenerateSharedProperties (shaderPropertiesVisitor, shaderPropertyUsagesVisitor, generationMode); foreach (var activeNode in activeNodeList) { if (activeNode is IGeneratesFunction) (activeNode as IGeneratesFunction).GenerateNodeFunction (shaderFunctionVisitor, generationMode); if (activeNode is IGeneratesVertexToFragmentBlock) (activeNode as IGeneratesVertexToFragmentBlock).GenerateVertexToFragmentBlock (shaderInputVisitor, generationMode); if (activeNode is IGeneratesBodyCode) (activeNode as IGeneratesBodyCode).GenerateNodeCode (shaderBodyVisitor, generationMode); if (activeNode is IGeneratesVertexShaderBlock) (activeNode as IGeneratesVertexShaderBlock).GenerateVertexShaderBlock (vertexShaderBlock, generationMode); activeNode.GeneratePropertyBlock (shaderPropertiesVisitor, generationMode); activeNode.GeneratePropertyUsages (shaderPropertyUsagesVisitor, generationMode); } if (shaderInputVisitor.numberOfChunks == 0) { shaderInputVisitor.AddShaderChunk("float4 color : COLOR;", true); } if (generationMode == GenerationMode.Preview2D) shaderBodyVisitor.AddShaderChunk ("return " + node.GetOutputVariableNameForSlot (node.outputSlots.FirstOrDefault (), generationMode) + ";", true); else shaderBodyVisitor.AddShaderChunk ("o.Emission = " + node.GetOutputVariableNameForSlot (node.outputSlots.FirstOrDefault (), generationMode) + ";", true); template = template.Replace ("${ShaderName}", shaderName); template = template.Replace ("${ShaderPropertiesHeader}", shaderPropertiesVisitor.GetShaderString (2)); template = template.Replace ("${ShaderPropertyUsages}", shaderPropertyUsagesVisitor.GetShaderString (3)); template = template.Replace ("${ShaderInputs}", shaderInputVisitor.GetShaderString (4)); template = template.Replace ("${ShaderFunctions}", shaderFunctionVisitor.GetShaderString (3)); template = template.Replace ("${VertexShaderBody}", vertexShaderBlock.GetShaderString (4)); template = template.Replace ("${PixelShaderBody}", shaderBodyVisitor.GetShaderString (4)); string vertexShaderBody = vertexShaderBlock.GetShaderString (4); if (vertexShaderBody.Length > 0) { template = template.Replace ("${VertexShaderDecl}", "vertex:vert"); template = template.Replace ("${VertexShaderBody}", vertexShaderBody); } else { template = template.Replace ("${VertexShaderDecl}", ""); template = template.Replace ("${VertexShaderBody}", vertexShaderBody); } return template; } public static void GenerateSurfaceShader(MaterialGraph graph) { string templateLocation = Path.Combine(Application.dataPath, Path.Combine("MaterialGraph", "shader.template")); if (!File.Exists(templateLocation)) return; var templateText = File.ReadAllText(templateLocation); var shaderBodyVisitor = new ShaderGenerator(); var shaderInputVisitor = new ShaderGenerator(); var shaderLightFunctionVisitor = new ShaderGenerator(); var shaderFunctionVisitor = new ShaderGenerator(); var shaderPropertiesVisitor = new PropertyGenerator(); var shaderPropertyUsagesVisitor = new ShaderGenerator(); var vertexShaderBlock = new ShaderGenerator(); (graph.currentGraph as PixelGraph).GenerateSurfaceShader( shaderBodyVisitor, shaderInputVisitor, shaderLightFunctionVisitor, shaderFunctionVisitor, shaderPropertiesVisitor, shaderPropertyUsagesVisitor, vertexShaderBlock); if (shaderInputVisitor.numberOfChunks == 0) { shaderInputVisitor.AddShaderChunk("float4 color : COLOR;", true); } var tagsVisitor = new ShaderGenerator(); var blendingVisitor = new ShaderGenerator(); var cullingVisitor = new ShaderGenerator(); var zTestVisitor = new ShaderGenerator(); var zWriteVisitor = new ShaderGenerator(); var options = graph.materialOptions; options.GetTags(tagsVisitor); options.GetBlend(blendingVisitor); options.GetCull(cullingVisitor); options.GetDepthTest(zTestVisitor); options.GetDepthWrite(zWriteVisitor); var resultShader = templateText.Replace("${ShaderName}", graph.name); resultShader = resultShader.Replace("${ShaderPropertiesHeader}", shaderPropertiesVisitor.GetShaderString(2)); resultShader = resultShader.Replace("${ShaderPropertyUsages}", shaderPropertyUsagesVisitor.GetShaderString(2)); resultShader = resultShader.Replace("${LightingFunctionName}", shaderLightFunctionVisitor.GetPragmaString()); resultShader = resultShader.Replace("${LightingFunction}", shaderLightFunctionVisitor.GetShaderString(2)); resultShader = resultShader.Replace("${ShaderFunctions}", shaderFunctionVisitor.GetShaderString(2)); resultShader = resultShader.Replace("${ShaderInputs}", shaderInputVisitor.GetShaderString(3)); resultShader = resultShader.Replace("${PixelShaderBody}", shaderBodyVisitor.GetShaderString(3)); resultShader = resultShader.Replace("${Tags}", tagsVisitor.GetShaderString(2)); resultShader = resultShader.Replace("${Blending}", blendingVisitor.GetShaderString(2)); resultShader = resultShader.Replace("${Culling}", cullingVisitor.GetShaderString(2)); resultShader = resultShader.Replace("${ZTest}", zTestVisitor.GetShaderString(2)); resultShader = resultShader.Replace("${ZWrite}", zWriteVisitor.GetShaderString(2)); string vertexShaderBody = vertexShaderBlock.GetShaderString(3); if (vertexShaderBody.Length > 0) { resultShader = resultShader.Replace("${VertexShaderDecl}", "vertex:vert"); resultShader = resultShader.Replace("${VertexShaderBody}", vertexShaderBody); } else { resultShader = resultShader.Replace("${VertexShaderDecl}", ""); resultShader = resultShader.Replace("${VertexShaderBody}", ""); } //Build a map of default graph.UpdateShaderSource(resultShader, shaderPropertiesVisitor.GetDefaultTexutres()); MaterialWindow.DebugMaterialGraph("----------Shader-----------"); MaterialWindow.DebugMaterialGraph(resultShader); } public int numberOfChunks { get { return m_ShaderChunks.Count; } } } }