您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
411 行
20 KiB
411 行
20 KiB
using System;
|
|
using System.Text;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text.RegularExpressions;
|
|
using UnityEditor.Graphing;
|
|
using UnityEditor.Graphing.Util;
|
|
using UnityEngine;
|
|
|
|
namespace UnityEditor.ShaderGraph
|
|
{
|
|
public static class GraphUtil
|
|
{
|
|
internal static string ConvertCamelCase(string text, bool preserveAcronyms)
|
|
{
|
|
if (string.IsNullOrEmpty(text))
|
|
return string.Empty;
|
|
StringBuilder newText = new StringBuilder(text.Length * 2);
|
|
newText.Append(text[0]);
|
|
for (int i = 1; i < text.Length; i++)
|
|
{
|
|
if (char.IsUpper(text[i]))
|
|
if ((text[i - 1] != ' ' && !char.IsUpper(text[i - 1])) ||
|
|
(preserveAcronyms && char.IsUpper(text[i - 1]) &&
|
|
i < text.Length - 1 && !char.IsUpper(text[i + 1])))
|
|
newText.Append(' ');
|
|
newText.Append(text[i]);
|
|
}
|
|
return newText.ToString();
|
|
}
|
|
|
|
public static void GenerateApplicationVertexInputs(ShaderGraphRequirements graphRequiements, ShaderGenerator vertexInputs)
|
|
{
|
|
vertexInputs.AddShaderChunk("struct GraphVertexInput", false);
|
|
vertexInputs.AddShaderChunk("{", false);
|
|
vertexInputs.Indent();
|
|
vertexInputs.AddShaderChunk("float4 vertex : POSITION;", false);
|
|
vertexInputs.AddShaderChunk("float3 normal : NORMAL;", false);
|
|
vertexInputs.AddShaderChunk("float4 tangent : TANGENT;", false);
|
|
|
|
if (graphRequiements.requiresVertexColor)
|
|
{
|
|
vertexInputs.AddShaderChunk("float4 color : COLOR;", false);
|
|
}
|
|
|
|
foreach (var channel in graphRequiements.requiresMeshUVs.Distinct())
|
|
vertexInputs.AddShaderChunk(string.Format("float4 texcoord{0} : TEXCOORD{0};", (int)channel), false);
|
|
|
|
vertexInputs.AddShaderChunk("UNITY_VERTEX_INPUT_INSTANCE_ID", true);
|
|
vertexInputs.Deindent();
|
|
vertexInputs.AddShaderChunk("};", false);
|
|
}
|
|
|
|
static void Visit(List<INode> outputList, Dictionary<Guid, INode> unmarkedNodes, INode node)
|
|
{
|
|
if (!unmarkedNodes.ContainsKey(node.guid))
|
|
return;
|
|
foreach (var slot in node.GetInputSlots<ISlot>())
|
|
{
|
|
foreach (var edge in node.owner.GetEdges(slot.slotReference))
|
|
{
|
|
var inputNode = node.owner.GetNodeFromGuid(edge.outputSlot.nodeGuid);
|
|
Visit(outputList, unmarkedNodes, inputNode);
|
|
}
|
|
}
|
|
unmarkedNodes.Remove(node.guid);
|
|
outputList.Add(node);
|
|
}
|
|
|
|
public static GenerationResults GetShader(this AbstractMaterialGraph graph, AbstractMaterialNode node, GenerationMode mode, string name)
|
|
{
|
|
var results = new GenerationResults();
|
|
bool isUber = node == null;
|
|
|
|
var vertexInputs = new ShaderGenerator();
|
|
var vertexShader = new ShaderGenerator();
|
|
var surfaceDescriptionFunction = new ShaderGenerator();
|
|
var surfaceDescriptionStruct = new ShaderGenerator();
|
|
var functionBuilder = new ShaderStringBuilder();
|
|
var functionRegistry = new FunctionRegistry(functionBuilder);
|
|
var surfaceInputs = new ShaderGenerator();
|
|
|
|
surfaceInputs.AddShaderChunk("struct SurfaceInputs{", false);
|
|
surfaceInputs.Indent();
|
|
|
|
var activeNodeList = ListPool<INode>.Get();
|
|
if (isUber)
|
|
{
|
|
var unmarkedNodes = graph.GetNodes<INode>().Where(x => !(x is IMasterNode)).ToDictionary(x => x.guid);
|
|
while (unmarkedNodes.Any())
|
|
{
|
|
var unmarkedNode = unmarkedNodes.FirstOrDefault();
|
|
Visit(activeNodeList, unmarkedNodes, unmarkedNode.Value);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NodeUtils.DepthFirstCollectNodesFromNode(activeNodeList, node);
|
|
}
|
|
|
|
var requirements = ShaderGraphRequirements.FromNodes(activeNodeList);
|
|
GenerateApplicationVertexInputs(requirements, vertexInputs);
|
|
ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(requirements.requiresNormal, InterpolatorType.Normal, surfaceInputs);
|
|
ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(requirements.requiresTangent, InterpolatorType.Tangent, surfaceInputs);
|
|
ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(requirements.requiresBitangent, InterpolatorType.BiTangent, surfaceInputs);
|
|
ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(requirements.requiresViewDir, InterpolatorType.ViewDirection, surfaceInputs);
|
|
ShaderGenerator.GenerateSpaceTranslationSurfaceInputs(requirements.requiresPosition, InterpolatorType.Position, surfaceInputs);
|
|
|
|
if (requirements.requiresVertexColor)
|
|
surfaceInputs.AddShaderChunk(String.Format("float4 {0};", ShaderGeneratorNames.VertexColor), false);
|
|
|
|
if (requirements.requiresScreenPosition)
|
|
surfaceInputs.AddShaderChunk(String.Format("float4 {0};", ShaderGeneratorNames.ScreenPosition), false);
|
|
|
|
results.previewMode = PreviewMode.Preview3D;
|
|
if (!isUber)
|
|
{
|
|
foreach (var pNode in activeNodeList.OfType<AbstractMaterialNode>())
|
|
{
|
|
if (pNode.previewMode == PreviewMode.Preview3D)
|
|
{
|
|
results.previewMode = PreviewMode.Preview3D;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (var channel in requirements.requiresMeshUVs.Distinct())
|
|
surfaceInputs.AddShaderChunk(String.Format("half4 {0};", channel.GetUVName()), false);
|
|
|
|
surfaceInputs.Deindent();
|
|
surfaceInputs.AddShaderChunk("};", false);
|
|
|
|
vertexShader.AddShaderChunk("GraphVertexInput PopulateVertexData(GraphVertexInput v){", false);
|
|
vertexShader.Indent();
|
|
vertexShader.AddShaderChunk("return v;", false);
|
|
vertexShader.Deindent();
|
|
vertexShader.AddShaderChunk("}", false);
|
|
|
|
var slots = new List<MaterialSlot>();
|
|
foreach (var activeNode in isUber ? activeNodeList.Where(n => ((AbstractMaterialNode)n).hasPreview) : ((INode)node).ToEnumerable())
|
|
{
|
|
if (activeNode is IMasterNode || activeNode is SubGraphOutputNode)
|
|
slots.AddRange(activeNode.GetInputSlots<MaterialSlot>());
|
|
else
|
|
slots.AddRange(activeNode.GetOutputSlots<MaterialSlot>());
|
|
}
|
|
GenerateSurfaceDescriptionStruct(surfaceDescriptionStruct, slots, !isUber);
|
|
|
|
var shaderProperties = new PropertyCollector();
|
|
results.outputIdProperty = new Vector1ShaderProperty
|
|
{
|
|
displayName = "OutputId",
|
|
generatePropertyBlock = false,
|
|
value = -1
|
|
};
|
|
if (isUber)
|
|
shaderProperties.AddShaderProperty(results.outputIdProperty);
|
|
|
|
GenerateSurfaceDescription(
|
|
activeNodeList,
|
|
node,
|
|
graph,
|
|
surfaceDescriptionFunction,
|
|
functionRegistry,
|
|
shaderProperties,
|
|
requirements,
|
|
mode,
|
|
outputIdProperty: results.outputIdProperty);
|
|
|
|
var finalBuilder = new ShaderStringBuilder();
|
|
finalBuilder.AppendLine(@"Shader ""{0}""", name);
|
|
using (finalBuilder.BlockScope())
|
|
{
|
|
finalBuilder.AppendLine("Properties");
|
|
using (finalBuilder.BlockScope())
|
|
{
|
|
finalBuilder.AppendLines(shaderProperties.GetPropertiesBlock(0));
|
|
}
|
|
|
|
finalBuilder.AppendLine(@"HLSLINCLUDE");
|
|
finalBuilder.AppendLine("#define USE_LEGACY_UNITY_MATRIX_VARIABLES");
|
|
finalBuilder.AppendLine(@"#include ""CoreRP/ShaderLibrary/Common.hlsl""");
|
|
finalBuilder.AppendLine(@"#include ""CoreRP/ShaderLibrary/Packing.hlsl""");
|
|
finalBuilder.AppendLine(@"#include ""CoreRP/ShaderLibrary/Color.hlsl""");
|
|
finalBuilder.AppendLine(@"#include ""CoreRP/ShaderLibrary/UnityInstancing.hlsl""");
|
|
finalBuilder.AppendLine(@"#include ""CoreRP/ShaderLibrary/EntityLighting.hlsl""");
|
|
finalBuilder.AppendLine(@"#include ""ShaderGraphLibrary/ShaderVariables.hlsl""");
|
|
finalBuilder.AppendLine(@"#include ""ShaderGraphLibrary/ShaderVariablesFunctions.hlsl""");
|
|
finalBuilder.AppendLine(@"#include ""ShaderGraphLibrary/Functions.hlsl""");
|
|
|
|
finalBuilder.AppendLines(shaderProperties.GetPropertiesDeclaration(0));
|
|
finalBuilder.AppendLines(surfaceInputs.GetShaderString(0));
|
|
finalBuilder.Concat(functionBuilder);
|
|
finalBuilder.AppendLines(vertexInputs.GetShaderString(0));
|
|
finalBuilder.AppendLines(surfaceDescriptionStruct.GetShaderString(0));
|
|
finalBuilder.AppendLines(vertexShader.GetShaderString(0));
|
|
finalBuilder.AppendLines(surfaceDescriptionFunction.GetShaderString(0));
|
|
finalBuilder.AppendLine(@"ENDHLSL");
|
|
|
|
finalBuilder.AppendLines(ShaderGenerator.GetPreviewSubShader(node, requirements));
|
|
ListPool<INode>.Release(activeNodeList);
|
|
}
|
|
|
|
results.configuredTextures = shaderProperties.GetConfiguredTexutres();
|
|
ShaderSourceMap sourceMap;
|
|
results.shader = finalBuilder.ToString(out sourceMap);
|
|
results.sourceMap = sourceMap;
|
|
return results;
|
|
}
|
|
|
|
public static void GenerateSurfaceDescriptionStruct(ShaderGenerator surfaceDescriptionStruct, List<MaterialSlot> slots, bool isMaster)
|
|
{
|
|
surfaceDescriptionStruct.AddShaderChunk("struct SurfaceDescription{", false);
|
|
surfaceDescriptionStruct.Indent();
|
|
if (isMaster)
|
|
{
|
|
foreach (var slot in slots)
|
|
surfaceDescriptionStruct.AddShaderChunk(String.Format("{0} {1};", NodeUtils.ConvertConcreteSlotValueTypeToString(AbstractMaterialNode.OutputPrecision.@float, slot.concreteValueType), NodeUtils.GetHLSLSafeName(slot.shaderOutputName)), false);
|
|
surfaceDescriptionStruct.Deindent();
|
|
}
|
|
else
|
|
{
|
|
surfaceDescriptionStruct.AddShaderChunk("float4 PreviewOutput;", false);
|
|
}
|
|
surfaceDescriptionStruct.Deindent();
|
|
surfaceDescriptionStruct.AddShaderChunk("};", false);
|
|
}
|
|
|
|
public static void GenerateSurfaceDescription(
|
|
List<INode> activeNodeList,
|
|
AbstractMaterialNode masterNode,
|
|
AbstractMaterialGraph graph,
|
|
ShaderGenerator surfaceDescriptionFunction,
|
|
FunctionRegistry functionRegistry,
|
|
PropertyCollector shaderProperties,
|
|
ShaderGraphRequirements requirements,
|
|
GenerationMode mode,
|
|
string functionName = "PopulateSurfaceData",
|
|
string surfaceDescriptionName = "SurfaceDescription",
|
|
Vector1ShaderProperty outputIdProperty = null,
|
|
IEnumerable<MaterialSlot> slots = null)
|
|
{
|
|
if (graph == null)
|
|
return;
|
|
|
|
surfaceDescriptionFunction.AddShaderChunk(String.Format("{0} {1}(SurfaceInputs IN) {{", surfaceDescriptionName, functionName), false);
|
|
surfaceDescriptionFunction.Indent();
|
|
surfaceDescriptionFunction.AddShaderChunk(String.Format("{0} surface = ({0})0;", surfaceDescriptionName), false);
|
|
|
|
graph.CollectShaderProperties(shaderProperties, mode);
|
|
|
|
foreach (var activeNode in activeNodeList.OfType<AbstractMaterialNode>())
|
|
{
|
|
if (activeNode is IGeneratesFunction)
|
|
{
|
|
functionRegistry.builder.currentNode = activeNode;
|
|
(activeNode as IGeneratesFunction).GenerateNodeFunction(functionRegistry, mode);
|
|
}
|
|
if (activeNode is IGeneratesBodyCode)
|
|
(activeNode as IGeneratesBodyCode).GenerateNodeCode(surfaceDescriptionFunction, mode);
|
|
if (masterNode == null && activeNode.hasPreview)
|
|
{
|
|
var outputSlot = activeNode.GetOutputSlots<MaterialSlot>().FirstOrDefault();
|
|
if (outputSlot != null)
|
|
surfaceDescriptionFunction.AddShaderChunk(String.Format("if ({0} == {1}) {{ surface.PreviewOutput = {2}; return surface; }}", outputIdProperty.referenceName, activeNode.tempId.index, ShaderGenerator.AdaptNodeOutputForPreview(activeNode, outputSlot.id, activeNode.GetVariableNameForSlot(outputSlot.id))), false);
|
|
}
|
|
|
|
// In case of the subgraph output node, the preview is generated
|
|
// from the first input to the node.
|
|
if (activeNode is SubGraphOutputNode)
|
|
{
|
|
var inputSlot = activeNode.GetInputSlots<MaterialSlot>().FirstOrDefault();
|
|
if (inputSlot != null)
|
|
{
|
|
var foundEdges = graph.GetEdges(inputSlot.slotReference).ToArray();
|
|
string slotValue = foundEdges.Any() ? activeNode.GetSlotValue(inputSlot.id, mode) : inputSlot.GetDefaultValue(mode);
|
|
surfaceDescriptionFunction.AddShaderChunk(String.Format("if ({0} == {1}) {{ surface.PreviewOutput = {2}; return surface; }}", outputIdProperty.referenceName, activeNode.tempId.index, slotValue), false);
|
|
}
|
|
}
|
|
|
|
activeNode.CollectShaderProperties(shaderProperties, mode);
|
|
}
|
|
functionRegistry.builder.currentNode = null;
|
|
|
|
if (masterNode != null)
|
|
{
|
|
if (masterNode is IMasterNode)
|
|
{
|
|
var usedSlots = slots ?? masterNode.GetInputSlots<MaterialSlot>();
|
|
foreach (var input in usedSlots)
|
|
{
|
|
var foundEdges = graph.GetEdges(input.slotReference).ToArray();
|
|
if (foundEdges.Any())
|
|
{
|
|
surfaceDescriptionFunction.AddShaderChunk(string.Format("surface.{0} = {1};", NodeUtils.GetHLSLSafeName(input.shaderOutputName), masterNode.GetSlotValue(input.id, mode)), true);
|
|
}
|
|
else
|
|
{
|
|
surfaceDescriptionFunction.AddShaderChunk(string.Format("surface.{0} = {1};", NodeUtils.GetHLSLSafeName(input.shaderOutputName), input.GetDefaultValue(mode)), true);
|
|
}
|
|
}
|
|
}
|
|
else if (masterNode.hasPreview)
|
|
{
|
|
foreach (var slot in masterNode.GetOutputSlots<MaterialSlot>())
|
|
surfaceDescriptionFunction.AddShaderChunk(string.Format("surface.{0} = {1};", NodeUtils.GetHLSLSafeName(slot.shaderOutputName), masterNode.GetSlotValue(slot.id, mode)), true);
|
|
}
|
|
}
|
|
|
|
surfaceDescriptionFunction.AddShaderChunk("return surface;", false);
|
|
surfaceDescriptionFunction.Deindent();
|
|
surfaceDescriptionFunction.AddShaderChunk("}", false);
|
|
}
|
|
|
|
public static GenerationResults GetPreviewShader(this AbstractMaterialGraph graph, AbstractMaterialNode node)
|
|
{
|
|
return graph.GetShader(node, GenerationMode.Preview, String.Format("hidden/preview/{0}", node.GetVariableNameForNode()));
|
|
}
|
|
|
|
public static GenerationResults GetUberPreviewShader(this AbstractMaterialGraph graph)
|
|
{
|
|
return graph.GetShader(null, GenerationMode.Preview, "hidden/preview");
|
|
}
|
|
|
|
static Dictionary<SerializationHelper.TypeSerializationInfo, SerializationHelper.TypeSerializationInfo> s_LegacyTypeRemapping;
|
|
|
|
public static Dictionary<SerializationHelper.TypeSerializationInfo, SerializationHelper.TypeSerializationInfo> GetLegacyTypeRemapping()
|
|
{
|
|
if (s_LegacyTypeRemapping == null)
|
|
{
|
|
s_LegacyTypeRemapping = new Dictionary<SerializationHelper.TypeSerializationInfo, SerializationHelper.TypeSerializationInfo>();
|
|
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
|
{
|
|
foreach (var type in assembly.GetTypesOrNothing())
|
|
{
|
|
if (type.IsAbstract)
|
|
continue;
|
|
foreach (var attribute in type.GetCustomAttributes(typeof(FormerNameAttribute), false))
|
|
{
|
|
var legacyAttribute = (FormerNameAttribute)attribute;
|
|
var serializationInfo = new SerializationHelper.TypeSerializationInfo { fullName = legacyAttribute.fullName };
|
|
s_LegacyTypeRemapping[serializationInfo] = SerializationHelper.GetTypeSerializableAsString(type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return s_LegacyTypeRemapping;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sanitizes a supplied string such that it does not collide
|
|
/// with any other name in a collection.
|
|
/// </summary>
|
|
/// <param name="existingNames">
|
|
/// A collection of names that the new name should not collide with.
|
|
/// </param>
|
|
/// <param name="duplicateFormat">
|
|
/// The format applied to the name if a duplicate exists.
|
|
/// This must be a format string that contains `{0}` and `{1}`
|
|
/// once each. An example could be `{0} ({1})`, which will append ` (n)`
|
|
/// to the name for the n`th duplicate.
|
|
/// </param>
|
|
/// <param name="name">
|
|
/// The name to be sanitized.
|
|
/// </param>
|
|
/// <returns>
|
|
/// A name that is distinct form any name in `existingNames`.
|
|
/// </returns>
|
|
internal static string SanitizeName(IEnumerable<string> existingNames, string duplicateFormat, string name)
|
|
{
|
|
if (!existingNames.Contains(name))
|
|
return name;
|
|
|
|
string escapedDuplicateFormat = Regex.Escape(duplicateFormat);
|
|
|
|
// Escaped format will escape string interpolation, so the escape caracters must be removed for these.
|
|
escapedDuplicateFormat = escapedDuplicateFormat.Replace(@"\{0}", @"{0}");
|
|
escapedDuplicateFormat = escapedDuplicateFormat.Replace(@"\{1}", @"{1}");
|
|
|
|
var baseRegex = new Regex(string.Format(escapedDuplicateFormat, @"^(.*)", @"(\d+)"));
|
|
|
|
var baseMatch = baseRegex.Match(name);
|
|
if (baseMatch.Success)
|
|
name = baseMatch.Groups[1].Value;
|
|
|
|
string baseNameExpression= string.Format(@"^{0}", Regex.Escape(name));
|
|
var regex = new Regex(string.Format(escapedDuplicateFormat, baseNameExpression, @"(\d+)") + "$");
|
|
|
|
var existingDuplicateNumbers = existingNames.Select(existingName => regex.Match(existingName)).Where(m => m.Success).Select(m => int.Parse(m.Groups[1].Value)).Where(n => n > 0).Distinct().ToList();
|
|
|
|
var duplicateNumber = 1;
|
|
existingDuplicateNumbers.Sort();
|
|
if (existingDuplicateNumbers.Any() && existingDuplicateNumbers.First() == 1)
|
|
{
|
|
duplicateNumber = existingDuplicateNumbers.Last() + 1;
|
|
for (var i = 1; i < existingDuplicateNumbers.Count; i++)
|
|
{
|
|
if (existingDuplicateNumbers[i - 1] != existingDuplicateNumbers[i] - 1)
|
|
{
|
|
duplicateNumber = existingDuplicateNumbers[i - 1] + 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return string.Format(duplicateFormat, name, duplicateNumber);
|
|
}
|
|
}
|
|
}
|