123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357 |
- using System;
- using System.IO;
- using System.Collections.Generic;
- using System.Linq;
- using UnityEngine;
- using UnityEditor.Graphing;
- using UnityEditor.Rendering;
- using UnityEngine.UIElements;
- using UnityEditor.ShaderGraph.Drawing;
- namespace UnityEditor.ShaderGraph
- {
- [HasDependencies(typeof(MinimalCustomFunctionNode))]
- [Title("Utility", "Custom Function")]
- class CustomFunctionNode : AbstractMaterialNode, IGeneratesBodyCode, IGeneratesFunction, IHasSettings
- {
- [Serializable]
- public class MinimalCustomFunctionNode : IHasDependencies
- {
- [SerializeField]
- HlslSourceType m_SourceType = HlslSourceType.File;
- [SerializeField]
- string m_FunctionName = k_DefaultFunctionName;
- [SerializeField]
- string m_FunctionSource = null;
- public void GetSourceAssetDependencies(List<string> paths)
- {
- if (m_SourceType == HlslSourceType.File)
- {
- m_FunctionSource = UpgradeFunctionSource(m_FunctionSource);
- if (IsValidFunction(m_SourceType, m_FunctionName, m_FunctionSource, null))
- {
- paths.Add(AssetDatabase.GUIDToAssetPath(m_FunctionSource));
- }
- }
- }
- }
-
- public static string[] s_ValidExtensions = { ".hlsl", ".cginc" };
- const string k_InvalidFileType = "Source file is not a valid file type. Valid file extensions are .hlsl and .cginc";
- const string k_MissingOutputSlot = "A Custom Function Node must have at least one output slot";
- public CustomFunctionNode()
- {
- name = "Custom Function";
- }
- public override bool hasPreview => true;
- [SerializeField]
- HlslSourceType m_SourceType = HlslSourceType.File;
- public HlslSourceType sourceType
- {
- get => m_SourceType;
- set => m_SourceType = value;
- }
- [SerializeField]
- string m_FunctionName = k_DefaultFunctionName;
- const string k_DefaultFunctionName = "Enter function name here...";
- public string functionName
- {
- get => m_FunctionName;
- set => m_FunctionName = value;
- }
- public static string defaultFunctionName => k_DefaultFunctionName;
- [SerializeField]
- string m_FunctionSource;
- const string k_DefaultFunctionSource = "Enter function source file path here...";
- public string functionSource
- {
- get => m_FunctionSource;
- set => m_FunctionSource = value;
- }
- [SerializeField]
- string m_FunctionBody = k_DefaultFunctionBody;
- const string k_DefaultFunctionBody = "Enter function body here...";
- public string functionBody
- {
- get => m_FunctionBody;
- set => m_FunctionBody = value;
- }
- public static string defaultFunctionBody => k_DefaultFunctionBody;
- public void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode generationMode)
- {
- List<MaterialSlot> slots = new List<MaterialSlot>();
- GetOutputSlots<MaterialSlot>(slots);
- if(!IsValidFunction())
- {
- if(generationMode == GenerationMode.Preview && slots.Count != 0)
- {
- slots.OrderBy(s => s.id);
- sb.AppendLine("{0} {1};",
- slots[0].concreteValueType.ToShaderString(),
- GetVariableNameForSlot(slots[0].id));
- }
- return;
- }
- foreach (var argument in slots)
- sb.AppendLine("{0} {1};",
- argument.concreteValueType.ToShaderString(),
- GetVariableNameForSlot(argument.id));
- string call = $"{functionName}_$precision(";
- bool first = true;
- slots.Clear();
- GetInputSlots<MaterialSlot>(slots);
- foreach (var argument in slots)
- {
- if (!first)
- call += ", ";
- first = false;
- call += SlotInputValue(argument, generationMode);
- }
- slots.Clear();
- GetOutputSlots<MaterialSlot>(slots);
- foreach (var argument in slots)
- {
- if (!first)
- call += ", ";
- first = false;
- call += GetVariableNameForSlot(argument.id);
- }
- call += ");";
- sb.AppendLine(call);
- }
- public void GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode)
- {
- if(!IsValidFunction())
- return;
- switch (sourceType)
- {
- case HlslSourceType.File:
- registry.ProvideFunction(functionSource, builder =>
- {
- string path = AssetDatabase.GUIDToAssetPath(functionSource);
- // This is required for upgrading without console errors
- if(string.IsNullOrEmpty(path))
- path = functionSource;
- string hash;
- try
- {
- hash = AssetDatabase.GetAssetDependencyHash(path).ToString();
- }
- catch
- {
- hash = "Failed to compute hash for include";
- }
- builder.AppendLine($"// {hash}");
- builder.AppendLine($"#include \"{path}\"");
- });
- break;
- case HlslSourceType.String:
- registry.ProvideFunction(functionName, builder =>
- {
- builder.AppendLine(GetFunctionHeader());
- using (builder.BlockScope())
- {
- builder.AppendLines(functionBody);
- }
- });
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
- }
- string GetFunctionHeader()
- {
- string header = $"void {functionName}_$precision(";
- var first = true;
- List<MaterialSlot> slots = new List<MaterialSlot>();
- GetInputSlots<MaterialSlot>(slots);
- foreach (var argument in slots)
- {
- if (!first)
- header += ", ";
- first = false;
- header += $"{argument.concreteValueType.ToShaderString()} {argument.shaderOutputName}";
- }
- slots.Clear();
- GetOutputSlots<MaterialSlot>(slots);
- foreach (var argument in slots)
- {
- if (!first)
- header += ", ";
- first = false;
- header += $"out {argument.concreteValueType.ToShaderString()} {argument.shaderOutputName}";
- }
- header += ")";
- return header;
- }
- string SlotInputValue(MaterialSlot port, GenerationMode generationMode)
- {
- IEdge[] edges = port.owner.owner.GetEdges(port.slotReference).ToArray();
- if (edges.Any())
- {
- var fromSocketRef = edges[0].outputSlot;
- var fromNode = owner.GetNodeFromGuid<AbstractMaterialNode>(fromSocketRef.nodeGuid);
- if (fromNode == null)
- return string.Empty;
- var slot = fromNode.FindOutputSlot<MaterialSlot>(fromSocketRef.slotId);
- if (slot == null)
- return string.Empty;
- return ShaderGenerator.AdaptNodeOutput(fromNode, slot.id, port.concreteValueType);
- }
- return port.GetDefaultValue(generationMode);
- }
-
- bool IsValidFunction()
- {
- return IsValidFunction(sourceType, functionName, functionSource, functionBody);
- }
- static bool IsValidFunction(HlslSourceType sourceType, string functionName, string functionSource, string functionBody)
- {
- bool validFunctionName = !string.IsNullOrEmpty(functionName) && functionName != k_DefaultFunctionName;
- if(sourceType == HlslSourceType.String)
- {
- bool validFunctionBody = !string.IsNullOrEmpty(functionBody) && functionBody != k_DefaultFunctionBody;
- return validFunctionName & validFunctionBody;
- }
- else
- {
- if(!validFunctionName || string.IsNullOrEmpty(functionSource) || functionSource == k_DefaultFunctionSource)
- return false;
- string path = AssetDatabase.GUIDToAssetPath(functionSource);
- if(string.IsNullOrEmpty(path))
- path = functionSource;
- string extension = Path.GetExtension(path);
- return s_ValidExtensions.Contains(extension);
- }
- }
- void ValidateSlotName()
- {
- List<MaterialSlot> slots = new List<MaterialSlot>();
- GetSlots(slots);
- foreach (var slot in slots)
- {
- var error = NodeUtils.ValidateSlotName(slot.RawDisplayName(), out string errorMessage);
- if (error)
- {
- owner.AddValidationError(tempId, errorMessage);
- break;
- }
- }
- }
- public override void ValidateNode()
- {
- if (!this.GetOutputSlots<MaterialSlot>().Any())
- {
- owner.AddValidationError(tempId, k_MissingOutputSlot, ShaderCompilerMessageSeverity.Warning);
- }
- if(sourceType == HlslSourceType.File)
- {
- if(!string.IsNullOrEmpty(functionSource))
- {
- string path = AssetDatabase.GUIDToAssetPath(functionSource);
- if(!string.IsNullOrEmpty(path))
- {
- string extension = path.Substring(path.LastIndexOf('.'));
- if(!s_ValidExtensions.Contains(extension))
- {
- owner.AddValidationError(tempId, k_InvalidFileType, ShaderCompilerMessageSeverity.Error);
- }
- }
- }
- }
- ValidateSlotName();
- base.ValidateNode();
- }
- public void Reload(HashSet<string> changedFileDependencies)
- {
- if (changedFileDependencies.Contains(m_FunctionSource))
- {
- owner.ClearErrorsForNode(this);
- ValidateNode();
- Dirty(ModificationScope.Graph);
- }
- }
- public VisualElement CreateSettingsElement()
- {
- PropertySheet ps = new PropertySheet();
- ps.Add(new ReorderableSlotListView(this, SlotType.Input));
- ps.Add(new ReorderableSlotListView(this, SlotType.Output));
- ps.Add(new HlslFunctionView(this));
- return ps;
- }
- public static string UpgradeFunctionSource(string functionSource)
- {
- // Handle upgrade from legacy asset path version
- // If functionSource is not empty or a guid then assume it is legacy version
- // If asset can be loaded from path then get its guid
- // Otherwise it was the default string so set to empty
- Guid guid;
- if(!string.IsNullOrEmpty(functionSource) && !Guid.TryParse(functionSource, out guid))
- {
- string guidString = string.Empty;
- TextAsset textAsset = AssetDatabase.LoadAssetAtPath<TextAsset>(functionSource);
- if(textAsset != null)
- {
- long localId;
- AssetDatabase.TryGetGUIDAndLocalFileIdentifier(textAsset, out guidString, out localId);
- }
- functionSource = guidString;
- }
- return functionSource;
- }
- public override void OnAfterDeserialize()
- {
- base.OnAfterDeserialize();
- functionSource = UpgradeFunctionSource(functionSource);
- }
- }
- }
|