initial commit

This commit is contained in:
Jo 2025-01-07 02:06:59 +01:00
parent 6715289efe
commit 788c3389af
37645 changed files with 2526849 additions and 80 deletions

View file

@ -0,0 +1,507 @@
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;
using System.Text;
namespace UnityEditor.ShaderGraph
{
[HasDependencies(typeof(MinimalCustomFunctionNode))]
[Title("Utility", "Custom Function")]
class CustomFunctionNode : AbstractMaterialNode, IGeneratesBodyCode, IGeneratesFunction, IMayRequireTransform
{
// 0 original version
// 1 differentiate between struct-based UnityTexture2D and bare Texture2D resources (for all texture and samplerstate resources)
public override int latestVersion => 1;
public override IEnumerable<int> allowedNodeVersions => new int[] { 1 };
[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(AssetCollection assetCollection)
{
if (m_SourceType == HlslSourceType.File)
{
m_FunctionSource = UpgradeFunctionSource(m_FunctionSource);
if (IsValidFunction(m_SourceType, m_FunctionName, m_FunctionSource, null))
{
if (GUID.TryParse(m_FunctionSource, out GUID guid))
{
// as this is just #included into the generated .shader file
// it doesn't actually need to be a dependency, other than for export package
assetCollection.AddAssetDependency(guid, AssetCollection.Flags.IncludeInExportPackage);
}
}
}
}
}
enum SourceFileStatus
{
Empty, // No File specified
DoesNotExist, // Either file doesn't exist (empty name) or guid points to a non-existant file
Invalid, // File exists but isn't of a valid type (such as wrong extension)
Valid
};
// With ShaderInclude asset type, it should no longer be necessary to soft-check the extension.
public static string[] s_ValidExtensions = { ".hlsl", ".cginc", ".cg" };
const string k_InvalidFileType = "Source file is not a valid file type. Valid file extensions are .hlsl, .cginc, and .cg";
const string k_MissingFile = "Source file does not exist. A valid .hlsl, .cginc, or .cg file must be referenced";
const string k_MissingOutputSlot = "A Custom Function Node must have at least one output slot";
public CustomFunctionNode()
{
UpdateNodeName();
synonyms = new string[] { "code", "HLSL" };
}
void UpdateNodeName()
{
if ((functionName == defaultFunctionName) || (functionName == null))
name = "Custom Function";
else
name = functionName + " (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;
UpdateNodeName();
}
}
public string hlslFunctionName
{
get => m_FunctionName + "_$precision";
}
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)
{
using (var inputSlots = PooledList<MaterialSlot>.Get())
using (var outputSlots = PooledList<MaterialSlot>.Get())
{
GetInputSlots<MaterialSlot>(inputSlots);
GetOutputSlots<MaterialSlot>(outputSlots);
if (!IsValidFunction())
{
// invalid functions generate special preview code.. (why?)
if (generationMode == GenerationMode.Preview && outputSlots.Count != 0)
{
outputSlots.OrderBy(s => s.id);
var hlslVariableType = outputSlots[0].concreteValueType.ToShaderString();
sb.AppendLine("{0} {1};",
hlslVariableType,
GetVariableNameForSlot(outputSlots[0].id));
}
return;
}
// declare output variables
foreach (var output in outputSlots)
{
sb.AppendLine("{0} {1};",
output.concreteValueType.ToShaderString(),
GetVariableNameForSlot(output.id));
if (output.bareResource)
AssignDefaultBareResource(output, sb);
}
// call function
sb.TryAppendIndentation();
sb.Append(hlslFunctionName);
sb.Append("(");
bool first = true;
foreach (var input in inputSlots)
{
if (!first)
sb.Append(", ");
first = false;
sb.Append(SlotInputValue(input, generationMode));
// fixup input for Bare types
if (input.bareResource)
{
if (input is SamplerStateMaterialSlot)
sb.Append(".samplerstate");
else
sb.Append(".tex");
}
}
foreach (var output in outputSlots)
{
if (!first)
sb.Append(", ");
first = false;
sb.Append(GetVariableNameForSlot(output.id));
// fixup output for Bare types
if (output.bareResource)
{
if (output is SamplerStateMaterialSlot)
sb.Append(".samplerstate");
else
sb.Append(".tex");
}
}
sb.Append(");");
sb.AppendNewLine();
}
}
void AssignDefaultBareResource(MaterialSlot slot, ShaderStringBuilder sb)
{
switch (slot.concreteValueType)
{
case ConcreteSlotValueType.Texture2D:
{
var slotVariable = GetVariableNameForSlot(slot.id);
sb.TryAppendIndentation();
sb.Append(slotVariable);
sb.Append(".samplerstate = default_sampler_Linear_Repeat;");
sb.AppendNewLine();
sb.TryAppendIndentation();
sb.Append(slotVariable);
sb.Append(".texelSize = float4(1.0f/128.0f, 1.0f/128.0f, 128.0f, 128.0f);");
sb.AppendNewLine();
sb.TryAppendIndentation();
sb.Append(slotVariable);
sb.Append(".scaleTranslate = float4(1.0f, 1.0f, 0.0f, 0.0f);");
sb.AppendNewLine();
}
break;
case ConcreteSlotValueType.Texture3D:
case ConcreteSlotValueType.Texture2DArray:
case ConcreteSlotValueType.Cubemap:
{
var slotVariable = GetVariableNameForSlot(slot.id);
sb.TryAppendIndentation();
sb.Append(slotVariable);
sb.Append(".samplerstate = default_sampler_Linear_Repeat;");
sb.AppendNewLine();
}
break;
}
}
public void GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode)
{
if (!IsValidFunction())
return;
switch (sourceType)
{
case HlslSourceType.File:
string path = AssetDatabase.GUIDToAssetPath(functionSource);
// This is required for upgrading without console errors
if (string.IsNullOrEmpty(path))
path = functionSource;
registry.RequiresIncludePath(path);
break;
case HlslSourceType.String:
registry.ProvideFunction(hlslFunctionName, builder =>
{
GetFunctionHeader(builder);
using (builder.BlockScope())
{
builder.AppendLines(functionBody);
}
});
break;
default:
throw new ArgumentOutOfRangeException();
}
}
void GetFunctionHeader(ShaderStringBuilder sb)
{
using (var inputSlots = PooledList<MaterialSlot>.Get())
using (var outputSlots = PooledList<MaterialSlot>.Get())
{
GetInputSlots(inputSlots);
GetOutputSlots(outputSlots);
sb.Append("void ");
sb.Append(hlslFunctionName);
sb.Append("(");
var first = true;
foreach (var argument in inputSlots)
{
if (!first)
sb.Append(", ");
first = false;
argument.AppendHLSLParameterDeclaration(sb, argument.shaderOutputName);
}
foreach (var argument in outputSlots)
{
if (!first)
sb.Append(", ");
first = false;
sb.Append("out ");
argument.AppendHLSLParameterDeclaration(sb, argument.shaderOutputName);
}
sb.Append(")");
}
}
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 = fromSocketRef.node;
if (fromNode == null)
return string.Empty;
return fromNode.GetOutputForSlot(fromSocketRef, port.concreteValueType, generationMode);
}
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()
{
using (var slots = PooledList<MaterialSlot>.Get())
{
GetSlots(slots);
foreach (var slot in slots)
{
// check for bad slot names
var error = NodeUtils.ValidateSlotName(slot.RawDisplayName(), out string errorMessage);
if (error)
{
owner.AddValidationError(objectId, errorMessage);
break;
}
}
}
}
void ValidateBareTextureSlots()
{
using (var outputSlots = PooledList<MaterialSlot>.Get())
{
GetOutputSlots(outputSlots);
foreach (var slot in outputSlots)
{
if (slot.bareResource)
{
owner.AddValidationError(objectId, "This node uses Bare Texture or SamplerState outputs, which may produce unexpected results when fed to other nodes. Please convert the node to use the non-Bare struct-based outputs (see the structs defined in com.unity.render-pipelines.core/ShaderLibrary/Texture.hlsl)", ShaderCompilerMessageSeverity.Warning);
break;
}
}
}
}
public override void ValidateNode()
{
bool hasAnyOutputs = this.GetOutputSlots<MaterialSlot>().Any();
if (sourceType == HlslSourceType.File)
{
SourceFileStatus fileStatus = SourceFileStatus.Empty;
if (!string.IsNullOrEmpty(functionSource))
{
string path = AssetDatabase.GUIDToAssetPath(functionSource);
if (!string.IsNullOrEmpty(path) && AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(path) != null)
{
string extension = path.Substring(path.LastIndexOf('.'));
if (!s_ValidExtensions.Contains(extension))
{
fileStatus = SourceFileStatus.Invalid;
}
else
{
fileStatus = SourceFileStatus.Valid;
}
}
else
fileStatus = SourceFileStatus.DoesNotExist;
}
if (fileStatus == SourceFileStatus.DoesNotExist || (fileStatus == SourceFileStatus.Empty && hasAnyOutputs))
owner.AddValidationError(objectId, k_MissingFile, ShaderCompilerMessageSeverity.Error);
else if (fileStatus == SourceFileStatus.Invalid)
owner.AddValidationError(objectId, k_InvalidFileType, ShaderCompilerMessageSeverity.Error);
else if (fileStatus == SourceFileStatus.Valid)
owner.ClearErrorsForNode(this);
}
if (!hasAnyOutputs)
{
owner.AddValidationError(objectId, k_MissingOutputSlot, ShaderCompilerMessageSeverity.Warning);
}
ValidateSlotName();
ValidateBareTextureSlots();
base.ValidateNode();
}
public bool Reload(HashSet<string> changedFileDependencyGUIDs)
{
if (changedFileDependencyGUIDs.Contains(m_FunctionSource))
{
owner.ClearErrorsForNode(this);
ValidateNode();
Dirty(ModificationScope.Graph);
return true;
}
return false;
}
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))
{
// not sure why we don't use AssetDatabase.AssetPathToGUID...
// I guess we are testing that it actually exists and can be loaded here before converting?
string guidString = string.Empty;
ShaderInclude shaderInclude = AssetDatabase.LoadAssetAtPath<ShaderInclude>(functionSource);
if (shaderInclude != null)
{
long localId;
AssetDatabase.TryGetGUIDAndLocalFileIdentifier(shaderInclude, out guidString, out localId);
}
functionSource = guidString;
}
return functionSource;
}
public override void OnAfterDeserialize()
{
base.OnAfterDeserialize();
functionSource = UpgradeFunctionSource(functionSource);
UpdateNodeName();
}
public override void OnAfterMultiDeserialize(string json)
{
if (sgVersion < 1)
{
// any Texture2D slots used prior to version 1 should be flagged as "bare" so we can
// generate backwards compatible code
var slots = new List<MaterialSlot>();
GetSlots(slots);
foreach (var slot in slots)
{
slot.bareResource = true;
}
ChangeVersion(1);
}
}
public NeededTransform[] RequiresTransform(ShaderStageCapability stageCapability = ShaderStageCapability.All)
{
return new[]
{
NeededTransform.ObjectToWorld,
NeededTransform.WorldToObject
};
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fe9ce2929aeb84ff4aca41505b173cec
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,185 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor.Graphing;
using UnityEditor.ShaderGraph.Serialization;
namespace UnityEditor.ShaderGraph
{
[Serializable]
[Title("Utility", "Dropdown")]
class DropdownNode : AbstractMaterialNode, IOnAssetEnabled, IGeneratesBodyCode
{
internal const int k_MinEnumEntries = 2;
public DropdownNode()
{
UpdateNodeAfterDeserialization();
}
[SerializeField]
JsonRef<ShaderDropdown> m_Dropdown;
public ShaderDropdown dropdown
{
get { return m_Dropdown; }
set
{
if (m_Dropdown == value)
return;
m_Dropdown = value;
m_Dropdown.value.displayNameUpdateTrigger += UpdateNodeDisplayName;
UpdateNode();
Dirty(ModificationScope.Topological);
}
}
public override bool canSetPrecision => false;
public override bool hasPreview => true;
public const int OutputSlotId = 0;
public override bool allowedInMainGraph { get => false; }
public void UpdateNodeDisplayName(string newDisplayName)
{
MaterialSlot foundSlot = FindSlot<MaterialSlot>(OutputSlotId);
if (foundSlot != null)
foundSlot.displayName = newDisplayName;
}
public void OnEnable()
{
UpdateNode();
}
public void UpdateNode()
{
name = dropdown.displayName;
UpdatePorts();
}
void UpdatePorts()
{
// Get slots
List<MaterialSlot> inputSlots = new List<MaterialSlot>();
GetInputSlots(inputSlots);
// Store the edges
Dictionary<MaterialSlot, List<IEdge>> edgeDict = new Dictionary<MaterialSlot, List<IEdge>>();
foreach (MaterialSlot slot in inputSlots)
edgeDict.Add(slot, (List<IEdge>)slot.owner.owner.GetEdges(slot.slotReference));
// Remove old slots
for (int i = 0; i < inputSlots.Count; i++)
{
RemoveSlot(inputSlots[i].id);
}
// Add output slot
AddSlot(new DynamicVectorMaterialSlot(OutputSlotId, "Out", "Out", SlotType.Output, Vector4.zero));
// Add input slots
int[] slotIds = new int[dropdown.entries.Count + 1];
slotIds[dropdown.entries.Count] = OutputSlotId;
for (int i = 0; i < dropdown.entries.Count; i++)
{
// Get slot based on entry id
MaterialSlot slot = inputSlots.Where(x =>
x.id == dropdown.entries[i].id &&
x.RawDisplayName() == dropdown.entries[i].displayName &&
x.shaderOutputName == dropdown.entries[i].displayName).FirstOrDefault();
if (slot == null)
{
slot = new DynamicVectorMaterialSlot(dropdown.entries[i].id, dropdown.entries[i].displayName, dropdown.entries[i].displayName, SlotType.Input, Vector4.zero);
}
AddSlot(slot);
slotIds[i] = dropdown.entries[i].id;
}
RemoveSlotsNameNotMatching(slotIds);
// Reconnect the edges
foreach (KeyValuePair<MaterialSlot, List<IEdge>> entry in edgeDict)
{
foreach (IEdge edge in entry.Value)
{
owner.Connect(edge.outputSlot, edge.inputSlot);
}
}
ValidateNode();
}
public void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode generationMode)
{
var outputSlot = FindOutputSlot<MaterialSlot>(OutputSlotId);
bool isGeneratingSubgraph = owner.isSubGraph && (generationMode != GenerationMode.Preview);
if (generationMode == GenerationMode.Preview || !isGeneratingSubgraph)
{
sb.AppendLine(string.Format($"{outputSlot.concreteValueType.ToShaderString()} {GetVariableNameForSlot(OutputSlotId)};"));
var value = GetSlotValue(GetSlotIdForActiveSelection(), generationMode);
sb.AppendLine(string.Format($"{GetVariableNameForSlot(OutputSlotId)} = {value};"));
}
else
{
// Iterate all entries in the dropdown
for (int i = 0; i < dropdown.entries.Count; i++)
{
if (i == 0)
{
sb.AppendLine(string.Format($"{outputSlot.concreteValueType.ToShaderString()} {GetVariableNameForSlot(OutputSlotId)};"));
sb.AppendLine($"if ({m_Dropdown.value.referenceName} == {i})");
}
else
{
sb.AppendLine($"else if ({m_Dropdown.value.referenceName} == {i})");
}
{
sb.AppendLine("{");
sb.IncreaseIndent();
var value = GetSlotValue(GetSlotIdForPermutation(new KeyValuePair<ShaderDropdown, int>(dropdown, i)), generationMode);
sb.AppendLine(string.Format($"{GetVariableNameForSlot(OutputSlotId)} = {value};"));
sb.DecreaseIndent();
sb.AppendLine("}");
}
if (i == dropdown.entries.Count - 1)
{
sb.AppendLine($"else");
sb.AppendLine("{");
sb.IncreaseIndent();
var value = GetSlotValue(GetSlotIdForPermutation(new KeyValuePair<ShaderDropdown, int>(dropdown, 0)), generationMode);
sb.AppendLine(string.Format($"{GetVariableNameForSlot(OutputSlotId)} = {value};"));
sb.DecreaseIndent();
sb.AppendLine("}");
}
}
}
}
public int GetSlotIdForPermutation(KeyValuePair<ShaderDropdown, int> permutation)
{
return permutation.Key.entries[permutation.Value].id;
}
public int GetSlotIdForActiveSelection()
{
return dropdown.entries[dropdown.value].id;
}
protected override void CalculateNodeHasError()
{
if (dropdown == null || !owner.dropdowns.Any(x => x == dropdown))
{
owner.AddConcretizationError(objectId, "Dropdown Node has no associated dropdown.");
hasError = true;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ec74026c92381d24bbc45e040822e702
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,207 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor.Graphing;
using UnityEditor.ShaderGraph.Serialization;
namespace UnityEditor.ShaderGraph
{
[Serializable]
[Title("Utility", "Keyword")]
class KeywordNode : AbstractMaterialNode, IOnAssetEnabled, IGeneratesBodyCode
{
internal const int k_MinEnumEntries = 2;
internal const int k_MaxEnumEntries = 8;
public KeywordNode()
{
UpdateNodeAfterDeserialization();
}
[SerializeField]
JsonRef<ShaderKeyword> m_Keyword;
public ShaderKeyword keyword
{
get { return m_Keyword; }
set
{
if (m_Keyword == value)
return;
m_Keyword = value;
m_Keyword.value.displayNameUpdateTrigger += UpdateNodeDisplayName;
UpdateNode();
Dirty(ModificationScope.Topological);
}
}
public override bool canSetPrecision => false;
public override bool hasPreview => true;
public const int OutputSlotId = 0;
public void UpdateNodeDisplayName(string newDisplayName)
{
MaterialSlot foundSlot = FindSlot<MaterialSlot>(OutputSlotId);
if (foundSlot != null)
foundSlot.displayName = newDisplayName;
}
public void OnEnable()
{
UpdateNode();
}
public void UpdateNode()
{
name = keyword.displayName;
UpdatePorts();
}
void UpdatePorts()
{
switch (keyword.keywordType)
{
case KeywordType.Boolean:
{
// Boolean type has preset slots
PooledList<MaterialSlot> temp = PooledList<MaterialSlot>.Get();
GetInputSlots(temp);
if (temp.Any())
{
temp.Dispose();
break;
}
else
{
temp.Dispose();
}
AddSlot(new DynamicVectorMaterialSlot(OutputSlotId, "Out", "Out", SlotType.Output, Vector4.zero));
AddSlot(new DynamicVectorMaterialSlot(1, "On", "On", SlotType.Input, Vector4.zero));
AddSlot(new DynamicVectorMaterialSlot(2, "Off", "Off", SlotType.Input, Vector4.zero));
RemoveSlotsNameNotMatching(new int[] { 0, 1, 2 });
break;
}
case KeywordType.Enum:
using (var inputSlots = PooledList<MaterialSlot>.Get())
using (var slotIDs = PooledList<int>.Get())
{
// Get slots
GetInputSlots(inputSlots);
// Add output slot
AddSlot(new DynamicVectorMaterialSlot(OutputSlotId, "Out", "Out", SlotType.Output, Vector4.zero));
slotIDs.Add(OutputSlotId);
// Add input slots
for (int i = 0; i < keyword.entries.Count; i++)
{
// Get slot based on entry id
MaterialSlot slot = inputSlots.Find(x =>
x.id == keyword.entries[i].id &&
x.RawDisplayName() == keyword.entries[i].displayName &&
x.shaderOutputName == keyword.entries[i].referenceName);
// If slot doesn't exist, it's new so create it
if (slot == null)
{
slot = new DynamicVectorMaterialSlot(keyword.entries[i].id, keyword.entries[i].displayName, keyword.entries[i].referenceName, SlotType.Input, Vector4.zero);
}
AddSlot(slot);
slotIDs.Add(keyword.entries[i].id);
}
RemoveSlotsNameNotMatching(slotIDs);
bool orderChanged = SetSlotOrder(slotIDs);
if (orderChanged)
{
// unfortunately there is no way to get the view to update slot order other than Topological
Dirty(ModificationScope.Topological);
}
break;
}
}
ValidateNode();
}
public void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode generationMode)
{
var outputSlot = FindOutputSlot<MaterialSlot>(OutputSlotId);
switch (keyword.keywordType)
{
case KeywordType.Boolean:
{
// Get values
var onValue = GetSlotValue(1, generationMode);
var offValue = GetSlotValue(2, generationMode);
// Append code
sb.AppendLine($"#if defined({keyword.referenceName})");
sb.AppendLine(string.Format($"{outputSlot.concreteValueType.ToShaderString()} {GetVariableNameForSlot(OutputSlotId)} = {onValue};"));
sb.AppendLine("#else");
sb.AppendLine(string.Format($"{outputSlot.concreteValueType.ToShaderString()} {GetVariableNameForSlot(OutputSlotId)} = {offValue};"));
sb.AppendLine("#endif");
break;
}
case KeywordType.Enum:
{
// Iterate all entries in the keyword
for (int i = 0; i < keyword.entries.Count; i++)
{
// Insert conditional
if (i == 0)
{
sb.AppendLine($"#if defined({keyword.referenceName}_{keyword.entries[i].referenceName})");
}
else if (i == keyword.entries.Count - 1)
{
sb.AppendLine("#else");
}
else
{
sb.AppendLine($"#elif defined({keyword.referenceName}_{keyword.entries[i].referenceName})");
}
// Append per-slot code
var value = GetSlotValue(GetSlotIdForPermutation(new KeyValuePair<ShaderKeyword, int>(keyword, i)), generationMode);
sb.AppendLine(string.Format($"{outputSlot.concreteValueType.ToShaderString()} {GetVariableNameForSlot(OutputSlotId)} = {value};"));
}
// End condition
sb.AppendLine("#endif");
break;
}
default:
throw new ArgumentOutOfRangeException();
}
}
public int GetSlotIdForPermutation(KeyValuePair<ShaderKeyword, int> permutation)
{
switch (permutation.Key.keywordType)
{
// Slot 0 is output
case KeywordType.Boolean:
return 1 + permutation.Value;
// Ids are stored manually as slots are added
case KeywordType.Enum:
return permutation.Key.entries[permutation.Value].id;
default:
throw new ArgumentOutOfRangeException();
}
}
protected override void CalculateNodeHasError()
{
if (keyword == null || !owner.keywords.Any(x => x == keyword))
{
owner.AddConcretizationError(objectId, "Keyword Node has no associated keyword.");
hasError = true;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 06a79a4c3d9dfd245a9c817c1fd44d20
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1c5103f75b5ec7445bb4c7b5a36f6aec
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,36 @@
using System.Reflection;
using UnityEngine;
namespace UnityEditor.ShaderGraph
{
[Title("Utility", "Logic", "All")]
class AllNode : CodeFunctionNode
{
public AllNode()
{
name = "All";
}
public override bool hasPreview
{
get { return false; }
}
protected override MethodInfo GetFunctionToConvert()
{
return GetType().GetMethod("Unity_All", BindingFlags.Static | BindingFlags.NonPublic);
}
static string Unity_All(
[Slot(0, Binding.None)] DynamicDimensionVector In,
[Slot(1, Binding.None)] out Boolean Out)
{
return
@"
{
Out = all(In);
}
";
}
}
}

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: aa03a542537ab4fb4a01a004efa97f58
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View file

@ -0,0 +1,37 @@
using System.Reflection;
using UnityEngine;
namespace UnityEditor.ShaderGraph
{
[Title("Utility", "Logic", "And")]
class AndNode : CodeFunctionNode
{
public AndNode()
{
name = "And";
}
public override bool hasPreview
{
get { return false; }
}
protected override MethodInfo GetFunctionToConvert()
{
return GetType().GetMethod("Unity_And", BindingFlags.Static | BindingFlags.NonPublic);
}
static string Unity_And(
[Slot(0, Binding.None)] Boolean A,
[Slot(1, Binding.None)] Boolean B,
[Slot(2, Binding.None)] out Boolean Out)
{
return
@"
{
Out = A && B;
}
";
}
}
}

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 34929a6d0bb40407fb00206ab0a10fa1
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View file

@ -0,0 +1,36 @@
using System.Reflection;
using UnityEngine;
namespace UnityEditor.ShaderGraph
{
[Title("Utility", "Logic", "Any")]
class AnyNode : CodeFunctionNode
{
public AnyNode()
{
name = "Any";
}
public override bool hasPreview
{
get { return false; }
}
protected override MethodInfo GetFunctionToConvert()
{
return GetType().GetMethod("Unity_Any", BindingFlags.Static | BindingFlags.NonPublic);
}
static string Unity_Any(
[Slot(0, Binding.None)] DynamicDimensionVector In,
[Slot(1, Binding.None)] out Boolean Out)
{
return
@"
{
Out = any(In);
}
";
}
}
}

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f3d072ddeeee848848f2e59dbb5cf1d9
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View file

@ -0,0 +1,34 @@
using System.Reflection;
using UnityEngine;
namespace UnityEditor.ShaderGraph
{
[Title("Utility", "Logic", "Branch")]
class BranchNode : CodeFunctionNode
{
public BranchNode()
{
name = "Branch";
synonyms = new string[] { "switch", "if", "else" };
}
protected override MethodInfo GetFunctionToConvert()
{
return GetType().GetMethod("Unity_Branch", BindingFlags.Static | BindingFlags.NonPublic);
}
static string Unity_Branch(
[Slot(0, Binding.None)] Boolean Predicate,
[Slot(1, Binding.None, 1, 1, 1, 1)] DynamicDimensionVector True,
[Slot(2, Binding.None, 0, 0, 0, 0)] DynamicDimensionVector False,
[Slot(3, Binding.None)] out DynamicDimensionVector Out)
{
return
@"
{
Out = Predicate ? True : False;
}
";
}
}
}

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 15ad3dc0ff22d7748af5e82ef5503d32
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View file

@ -0,0 +1,59 @@
using System;
using System.Linq;
using UnityEditor.Graphing;
using UnityEditor.Rendering;
namespace UnityEditor.ShaderGraph
{
[Serializable]
[Title("Utility", "Logic", "Branch On Input Connection")]
class BranchOnInputConnectionNode : AbstractMaterialNode, IGeneratesBodyCode
{
public const int InputSlotId = 0;
public const int ConnectedSlotId = 1;
public const int NotConnectedSlotId = 2;
public const int OutSlotId = 3;
const string kInputSlotName = "Input";
const string kConnectedSlotName = "Connected";
const string kNotConnectedSlotName = "NotConnected";
const string kOutSlotName = "Out";
public BranchOnInputConnectionNode()
{
name = "Branch On Input Connection";
UpdateNodeAfterDeserialization();
}
public sealed override void UpdateNodeAfterDeserialization()
{
AddSlot(new PropertyConnectionStateMaterialSlot(InputSlotId, kInputSlotName, kInputSlotName, Graphing.SlotType.Input));
AddSlot(new DynamicVectorMaterialSlot(ConnectedSlotId, kConnectedSlotName, kConnectedSlotName, Graphing.SlotType.Input, UnityEngine.Vector4.one));
AddSlot(new DynamicVectorMaterialSlot(NotConnectedSlotId, kNotConnectedSlotName, kNotConnectedSlotName, Graphing.SlotType.Input, UnityEngine.Vector4.zero));
AddSlot(new DynamicVectorMaterialSlot(OutSlotId, kOutSlotName, kOutSlotName, Graphing.SlotType.Output, UnityEngine.Vector4.zero));
RemoveSlotsNameNotMatching(new[] { InputSlotId, ConnectedSlotId, NotConnectedSlotId, OutSlotId });
}
public override bool allowedInMainGraph => false;
public override bool hasPreview => true;
public void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode generationMode)
{
// inspect one of the dynamic slots to figure out what type we are actually using
var dynSlot = this.FindInputSlot<DynamicVectorMaterialSlot>(ConnectedSlotId);
string dynamicDimension = NodeUtils.GetSlotDimension(dynSlot.concreteValueType);
var dynamicType = "$precision" + dynamicDimension;
// declare output variable
var input = GetSlotValue(InputSlotId, generationMode);
var connected = GetSlotValue(ConnectedSlotId, generationMode);
var notconnected = GetSlotValue(NotConnectedSlotId, generationMode);
var output = GetVariableNameForSlot(OutSlotId);
if (generationMode == GenerationMode.Preview)
sb.AppendLine($"{dynamicType} {output} = {notconnected};");
else
sb.AppendLine($"{dynamicType} {output} = {input} ? {connected} : {notconnected};");
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: be7845eb62257564492a5c3ba1d0d181
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,146 @@
using System.Reflection;
using UnityEngine;
using UnityEditor.Graphing;
using UnityEditor.ShaderGraph.Drawing.Controls;
namespace UnityEditor.ShaderGraph
{
enum ComparisonType
{
Equal,
NotEqual,
Less,
LessOrEqual,
Greater,
GreaterOrEqual
};
[Title("Utility", "Logic", "Comparison")]
class ComparisonNode : CodeFunctionNode
{
public ComparisonNode()
{
name = "Comparison";
synonyms = new string[] { "equal", "greater than", "less than" };
}
[SerializeField]
private ComparisonType m_ComparisonType = ComparisonType.Equal;
[EnumControl("")]
public ComparisonType comparisonType
{
get { return m_ComparisonType; }
set
{
if (m_ComparisonType == value)
return;
m_ComparisonType = value;
Dirty(ModificationScope.Graph);
}
}
public override bool hasPreview
{
get { return false; }
}
protected override MethodInfo GetFunctionToConvert()
{
switch (comparisonType)
{
case ComparisonType.NotEqual:
return GetType().GetMethod("Unity_Comparison_NotEqual", BindingFlags.Static | BindingFlags.NonPublic);
case ComparisonType.Less:
return GetType().GetMethod("Unity_Comparison_Less", BindingFlags.Static | BindingFlags.NonPublic);
case ComparisonType.LessOrEqual:
return GetType().GetMethod("Unity_Comparison_LessOrEqual", BindingFlags.Static | BindingFlags.NonPublic);
case ComparisonType.Greater:
return GetType().GetMethod("Unity_Comparison_Greater", BindingFlags.Static | BindingFlags.NonPublic);
case ComparisonType.GreaterOrEqual:
return GetType().GetMethod("Unity_Comparison_GreaterOrEqual", BindingFlags.Static | BindingFlags.NonPublic);
default:
return GetType().GetMethod("Unity_Comparison_Equal", BindingFlags.Static | BindingFlags.NonPublic);
}
}
static string Unity_Comparison_Equal(
[Slot(0, Binding.None)] Vector1 A,
[Slot(1, Binding.None)] Vector1 B,
[Slot(2, Binding.None)] out Boolean Out)
{
return
@"
{
Out = A == B ? 1 : 0;
}
";
}
static string Unity_Comparison_NotEqual(
[Slot(0, Binding.None)] Vector1 A,
[Slot(1, Binding.None)] Vector1 B,
[Slot(2, Binding.None)] out Boolean Out)
{
return
@"
{
Out = A != B ? 1 : 0;
}
";
}
static string Unity_Comparison_Less(
[Slot(0, Binding.None)] Vector1 A,
[Slot(1, Binding.None)] Vector1 B,
[Slot(2, Binding.None)] out Boolean Out)
{
return
@"
{
Out = A < B ? 1 : 0;
}
";
}
static string Unity_Comparison_LessOrEqual(
[Slot(0, Binding.None)] Vector1 A,
[Slot(1, Binding.None)] Vector1 B,
[Slot(2, Binding.None)] out Boolean Out)
{
return
@"
{
Out = A <= B ? 1 : 0;
}
";
}
static string Unity_Comparison_Greater(
[Slot(0, Binding.None)] Vector1 A,
[Slot(1, Binding.None)] Vector1 B,
[Slot(2, Binding.None)] out Boolean Out)
{
return
@"
{
Out = A > B ? 1 : 0;
}
";
}
static string Unity_Comparison_GreaterOrEqual(
[Slot(0, Binding.None)] Vector1 A,
[Slot(1, Binding.None)] Vector1 B,
[Slot(2, Binding.None)] out Boolean Out)
{
return
@"
{
Out = A >= B ? 1 : 0;
}
";
}
}
}

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c66cc68d0d0862b4c8ddfc00093d0ae0
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View file

@ -0,0 +1,37 @@
using UnityEngine;
using UnityEditor.Graphing;
namespace UnityEditor.ShaderGraph
{
[Title("Utility", "Logic", "Is Front Face")]
class IsFrontFaceNode : AbstractMaterialNode, IGeneratesBodyCode, IMayRequireFaceSign
{
public IsFrontFaceNode()
{
name = "Is Front Face";
synonyms = new string[] { "face", "side" };
UpdateNodeAfterDeserialization();
}
public override bool hasPreview { get { return false; } }
public const int OutputSlotId = 0;
private const string kOutputSlotName = "Out";
public override void UpdateNodeAfterDeserialization()
{
AddSlot(new BooleanMaterialSlot(OutputSlotId, kOutputSlotName, kOutputSlotName, SlotType.Output, true, ShaderStageCapability.Fragment));
RemoveSlotsNameNotMatching(new[] { OutputSlotId });
}
public void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode generationMode)
{
sb.AppendLine(string.Format("$precision {0} = max(0, IN.{1}.x);", GetVariableNameForSlot(OutputSlotId), ShaderGeneratorNames.FaceSign));
}
public bool RequiresFaceSign(ShaderStageCapability stageCapability = ShaderStageCapability.Fragment)
{
return true;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c7e3dd61523f54e52ade29765e299d3f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,36 @@
using System.Reflection;
using UnityEngine;
namespace UnityEditor.ShaderGraph
{
[Title("Utility", "Logic", "Is Infinite")]
class IsInfiniteNode : CodeFunctionNode
{
public IsInfiniteNode()
{
name = "Is Infinite";
}
public override bool hasPreview
{
get { return false; }
}
protected override MethodInfo GetFunctionToConvert()
{
return GetType().GetMethod("Unity_IsInfinite", BindingFlags.Static | BindingFlags.NonPublic);
}
static string Unity_IsInfinite(
[Slot(0, Binding.None)] Vector1 In,
[Slot(1, Binding.None)] out Boolean Out)
{
return
@"
{
Out = isinf(In);
}
";
}
}
}

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6eaf1bad910084b85ac9ead4319c7820
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View file

@ -0,0 +1,36 @@
using System.Reflection;
using UnityEngine;
namespace UnityEditor.ShaderGraph
{
[Title("Utility", "Logic", "Is NaN")]
class IsNanNode : CodeFunctionNode
{
public IsNanNode()
{
name = "Is NaN";
}
public override bool hasPreview
{
get { return false; }
}
protected override MethodInfo GetFunctionToConvert()
{
return GetType().GetMethod("Unity_IsNaN", BindingFlags.Static | BindingFlags.NonPublic);
}
static string Unity_IsNaN(
[Slot(0, Binding.None)] Vector1 In,
[Slot(1, Binding.None)] out Boolean Out)
{
return
@"
{
Out = isnan(In) ? 1 : 0;
}
";
}
}
}

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0e15b8359c9db4d0ea27f4deab94137e
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View file

@ -0,0 +1,37 @@
using System.Reflection;
using UnityEngine;
namespace UnityEditor.ShaderGraph
{
[Title("Utility", "Logic", "Nand")]
class NandNode : CodeFunctionNode
{
public NandNode()
{
name = "Nand";
}
public override bool hasPreview
{
get { return false; }
}
protected override MethodInfo GetFunctionToConvert()
{
return GetType().GetMethod("Unity_Nand", BindingFlags.Static | BindingFlags.NonPublic);
}
static string Unity_Nand(
[Slot(0, Binding.None)] Boolean A,
[Slot(1, Binding.None)] Boolean B,
[Slot(2, Binding.None)] out Boolean Out)
{
return
@"
{
Out = !A && !B;
}
";
}
}
}

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4defde87a9d5e44d8b8900d3f8ef6440
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View file

@ -0,0 +1,36 @@
using System.Reflection;
using UnityEngine;
namespace UnityEditor.ShaderGraph
{
[Title("Utility", "Logic", "Not")]
class NotNode : CodeFunctionNode
{
public NotNode()
{
name = "Not";
}
public override bool hasPreview
{
get { return false; }
}
protected override MethodInfo GetFunctionToConvert()
{
return GetType().GetMethod("Unity_Not", BindingFlags.Static | BindingFlags.NonPublic);
}
static string Unity_Not(
[Slot(0, Binding.None)] Boolean In,
[Slot(1, Binding.None)] out Boolean Out)
{
return
@"
{
Out = !In;
}
";
}
}
}

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: acb4138f9cef644dfa59102e871d1820
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View file

@ -0,0 +1,37 @@
using System.Reflection;
using UnityEngine;
namespace UnityEditor.ShaderGraph
{
[Title("Utility", "Logic", "Or")]
class OrNode : CodeFunctionNode
{
public OrNode()
{
name = "Or";
}
public override bool hasPreview
{
get { return false; }
}
protected override MethodInfo GetFunctionToConvert()
{
return GetType().GetMethod("Unity_Or", BindingFlags.Static | BindingFlags.NonPublic);
}
static string Unity_Or(
[Slot(0, Binding.None)] Boolean A,
[Slot(1, Binding.None)] Boolean B,
[Slot(2, Binding.None)] out Boolean Out)
{
return
@"
{
Out = A || B;
}
";
}
}
}

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0f6f860c0f1e24214b5d2974cc624227
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View file

@ -0,0 +1,60 @@
using System.Reflection;
using UnityEngine;
namespace UnityEditor.ShaderGraph
{
[Title("Utility", "Preview")]
class PreviewNode : CodeFunctionNode
{
public override bool hasPreview { get { return true; } }
[SerializeField]
float m_Width;
[SerializeField]
float m_Height;
public void SetDimensions(float width, float height)
{
float newSize = Mathf.Clamp(Mathf.Min(width, height), 150f, 1000f);
m_Width = newSize;
m_Height = newSize;
}
public float width
{
get { return m_Width; }
}
public float height
{
get { return m_Height; }
}
public PreviewNode()
{
name = "Preview";
m_Width = 208f;
m_Height = 208f;
}
protected override MethodInfo GetFunctionToConvert()
{
return GetType().GetMethod("Unity_Preview", BindingFlags.Static | BindingFlags.NonPublic);
}
static string Unity_Preview(
[Slot(0, Binding.None)] DynamicDimensionVector In,
[Slot(1, Binding.None)] out DynamicDimensionVector Out)
{
return
@"
{
Out = In;
}
";
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0a9c3dd2c3b0c4347876f3e448be2046
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,188 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Graphing;
using UnityEditor.Rendering;
using UnityEngine;
namespace UnityEditor.ShaderGraph
{
class RedirectNodeData : AbstractMaterialNode
{
public const int kInputSlotID = 0;
public const int kOutputSlotID = 1;
public RedirectNodeData()
{
name = "Redirect Node";
}
// Static version for testability
public static RedirectNodeData Create(GraphData graph, SlotValueType edgeType, Vector2 absolutePosition, SlotReference inputRef, SlotReference outputRef, GroupData group)
{
var nodeData = new RedirectNodeData();
nodeData.AddSlots(edgeType);
nodeData.SetPosition(absolutePosition);
nodeData.group = group;
// Hard-coded for single input-output. Changes would be needed for multi-input redirects
var nodeInSlotRef = nodeData.GetSlotReference(RedirectNodeData.kInputSlotID);
var nodeOutSlotRef = nodeData.GetSlotReference(RedirectNodeData.kOutputSlotID);
graph.owner.RegisterCompleteObjectUndo("Add Redirect Node");
graph.AddNode(nodeData);
graph.Connect(outputRef, nodeInSlotRef);
graph.Connect(nodeOutSlotRef, inputRef);
return nodeData;
}
void AddSlots(SlotValueType edgeType)
{
// Valuetype gets the type should be the type for input and output
switch (edgeType)
{
case SlotValueType.Boolean:
AddSlot(new BooleanMaterialSlot(kInputSlotID, "", "", SlotType.Input, false));
AddSlot(new BooleanMaterialSlot(kOutputSlotID, "", "", SlotType.Output, false));
break;
case SlotValueType.Vector1:
AddSlot(new DynamicVectorMaterialSlot(kInputSlotID, "", "", SlotType.Input, Vector4.zero));
AddSlot(new DynamicVectorMaterialSlot(kOutputSlotID, "", "", SlotType.Output, Vector4.zero));
break;
case SlotValueType.Vector2:
AddSlot(new DynamicVectorMaterialSlot(kInputSlotID, "", "", SlotType.Input, Vector4.zero));
AddSlot(new DynamicVectorMaterialSlot(kOutputSlotID, "", "", SlotType.Output, Vector4.zero));
break;
case SlotValueType.Vector3:
AddSlot(new DynamicVectorMaterialSlot(kInputSlotID, "", "", SlotType.Input, Vector4.zero));
AddSlot(new DynamicVectorMaterialSlot(kOutputSlotID, "", "", SlotType.Output, Vector4.zero));
break;
case SlotValueType.Vector4:
AddSlot(new DynamicVectorMaterialSlot(kInputSlotID, "", "", SlotType.Input, Vector4.zero));
AddSlot(new DynamicVectorMaterialSlot(kOutputSlotID, "", "", SlotType.Output, Vector4.zero));
break;
case SlotValueType.Matrix2:
AddSlot(new DynamicMatrixMaterialSlot(kInputSlotID, "", "", SlotType.Input));
AddSlot(new DynamicMatrixMaterialSlot(kOutputSlotID, "", "", SlotType.Output));
break;
case SlotValueType.Matrix3:
AddSlot(new DynamicMatrixMaterialSlot(kInputSlotID, "", "", SlotType.Input));
AddSlot(new DynamicMatrixMaterialSlot(kOutputSlotID, "", "", SlotType.Output));
break;
case SlotValueType.Matrix4:
AddSlot(new DynamicMatrixMaterialSlot(kInputSlotID, "", "", SlotType.Input));
AddSlot(new DynamicMatrixMaterialSlot(kOutputSlotID, "", "", SlotType.Output));
break;
case SlotValueType.Texture2D:
AddSlot(new Texture2DMaterialSlot(kInputSlotID, "", "", SlotType.Input));
AddSlot(new Texture2DMaterialSlot(kOutputSlotID, "", "", SlotType.Output));
break;
case SlotValueType.Texture2DArray:
AddSlot(new Texture2DArrayMaterialSlot(kInputSlotID, "", "", SlotType.Input));
AddSlot(new Texture2DArrayMaterialSlot(kOutputSlotID, "", "", SlotType.Output));
break;
case SlotValueType.Texture3D:
AddSlot(new Texture3DMaterialSlot(kInputSlotID, "", "", SlotType.Input));
AddSlot(new Texture3DMaterialSlot(kOutputSlotID, "", "", SlotType.Output));
break;
case SlotValueType.Cubemap:
AddSlot(new CubemapMaterialSlot(kInputSlotID, "", "", SlotType.Input));
AddSlot(new CubemapMaterialSlot(kOutputSlotID, "", "", SlotType.Output));
break;
case SlotValueType.SamplerState:
AddSlot(new SamplerStateMaterialSlot(kInputSlotID, "", "", SlotType.Input));
AddSlot(new SamplerStateMaterialSlot(kOutputSlotID, "", "", SlotType.Output));
break;
case SlotValueType.Gradient:
AddSlot(new GradientMaterialSlot(kInputSlotID, "", "", SlotType.Input));
AddSlot(new GradientMaterialSlot(kOutputSlotID, "", "", SlotType.Output));
break;
case SlotValueType.Dynamic:
AddSlot(new DynamicValueMaterialSlot(kInputSlotID, "", "", SlotType.Input, Matrix4x4.zero));
AddSlot(new DynamicValueMaterialSlot(kOutputSlotID, "", "", SlotType.Output, Matrix4x4.zero));
break;
case SlotValueType.DynamicMatrix:
AddSlot(new DynamicMatrixMaterialSlot(kInputSlotID, "", "", SlotType.Input));
AddSlot(new DynamicMatrixMaterialSlot(kOutputSlotID, "", "", SlotType.Output));
break;
case SlotValueType.DynamicVector:
AddSlot(new DynamicVectorMaterialSlot(kInputSlotID, "", "", SlotType.Input, Vector4.zero));
AddSlot(new DynamicVectorMaterialSlot(kOutputSlotID, "", "", SlotType.Output, Vector4.zero));
break;
case SlotValueType.VirtualTexture:
AddSlot(new VirtualTextureMaterialSlot(kInputSlotID, "", "", SlotType.Input));
AddSlot(new VirtualTextureMaterialSlot(kOutputSlotID, "", "", SlotType.Output));
break;
default:
throw new ArgumentOutOfRangeException();
}
}
protected internal override string GetOutputForSlot(SlotReference fromSocketRef, ConcreteSlotValueType valueType, GenerationMode generationMode)
{
var slotRef = NodeUtils.DepthFirstCollectRedirectNodeFromNode(this);
var fromLeftNode = slotRef.node;
if (fromLeftNode is RedirectNodeData)
{
return GetSlotValue(kInputSlotID, generationMode);
}
if (fromLeftNode != null)
{
return GenerationUtils.AdaptNodeOutput(fromLeftNode, slotRef.slotId, valueType);
}
return base.GetOutputForSlot(fromSocketRef, valueType, generationMode);
}
public void SetPosition(Vector2 pos)
{
var temp = drawState;
Vector2 offset = new Vector2(-30, -12);
temp.position = new Rect(pos + offset, Vector2.zero);
drawState = temp;
}
public void GetOutputAndInputSlots(out SlotReference outputSlotRef, out List<SlotReference> inputSlotRefs)
{
var inputSlot = FindSlot<MaterialSlot>(kInputSlotID);
var inEdges = owner.GetEdges(inputSlot.slotReference).ToList();
outputSlotRef = inEdges.Any() ? inEdges.First().outputSlot : new SlotReference();
var outputSlot = FindSlot<MaterialSlot>(kOutputSlotID);
// Get the slot where this edge ends.
var outEdges = owner.GetEdges(outputSlot.slotReference);
inputSlotRefs = new List<SlotReference>(outEdges.Select(edge => edge.inputSlot));
}
public override void ValidateNode()
{
base.ValidateNode();
bool noInputs = false;
bool noOutputs = false;
var slots = new List<MaterialSlot>();
GetInputSlots(slots);
foreach (var inSlot in slots)
{
var edges = owner.GetEdges(inSlot.slotReference).ToList();
noInputs = !edges.Any();
}
slots.Clear();
GetOutputSlots(slots);
foreach (var outSlot in slots)
{
var edges = owner.GetEdges(outSlot.slotReference).ToList();
noOutputs = !edges.Any();
}
if (noInputs && !noOutputs)
{
owner.AddValidationError(objectId, "Node has no inputs and default value will be 0.", ShaderCompilerMessageSeverity.Warning);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b70493ee47ef82b46be4fe3568b0bb2d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,172 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UIElements;
using UnityEditor.Graphing;
using UnityEditor.ShaderGraph.Drawing;
using UnityEditor.Experimental.GraphView;
using UnityEditor.Rendering;
namespace UnityEditor.ShaderGraph
{
class RedirectNodeView : RedirectNode, IShaderNodeView
{
IEdgeConnectorListener m_ConnectorListener;
// Tie the nodeView to its data
public void ConnectToData(AbstractMaterialNode inNode, IEdgeConnectorListener connectorListener)
{
if (inNode == null)
return;
// Set references
node = inNode;
title = "";
m_ConnectorListener = connectorListener;
viewDataKey = node.objectId;
// Set the VisualElement's position
SetPosition(new Rect(node.drawState.position.x, node.drawState.position.y, 0, 0));
AddSlots(node.GetSlots<MaterialSlot>());
// Removing a divider that made the ui a bit ugly
VisualElement contents = mainContainer.Q("contents");
VisualElement divider = contents?.Q("divider");
if (divider != null)
{
divider.RemoveFromHierarchy();
}
}
public void AddSlots(IEnumerable<MaterialSlot> slots)
{
foreach (var slot in slots)
{
if (slot.hidden)
continue;
var port = ShaderPort.Create(slot, m_ConnectorListener);
if (slot.isOutputSlot)
outputContainer.Add(port);
else
inputContainer.Add(port);
}
}
#region IShaderNodeView interface
public Node gvNode => this;
public AbstractMaterialNode node { get; private set; }
public VisualElement colorElement { get { return this; } }
public void Dispose()
{
node = null;
userData = null;
}
public void UpdatePortInputTypes()
{
foreach (var anchor in inputContainer.Children().Concat(outputContainer.Children()).OfType<ShaderPort>())
{
var slot = anchor.slot;
anchor.portName = slot.displayName;
anchor.visualClass = slot.concreteValueType.ToClassName();
}
}
public void OnModified(ModificationScope scope)
{
if (scope == ModificationScope.Topological)
{
var slots = node.GetSlots<MaterialSlot>().ToList();
var inputPorts = inputContainer.Children().OfType<ShaderPort>().ToList();
foreach (var port in inputPorts)
{
var currentSlot = port.slot;
var newSlot = slots.FirstOrDefault(s => s.id == currentSlot.id);
if (newSlot == null)
{
// Slot doesn't exist anymore, remove it
inputContainer.Remove(port);
}
else
{
port.slot = newSlot;
slots.Remove(newSlot);
}
}
var outputPorts = outputContainer.Children().OfType<ShaderPort>().ToList();
foreach (var port in outputPorts)
{
var currentSlot = port.slot;
var newSlot = slots.FirstOrDefault(s => s.id == currentSlot.id);
if (newSlot == null)
{
outputContainer.Remove(port);
}
else
{
port.slot = newSlot;
slots.Remove(newSlot);
}
}
AddSlots(slots);
slots.Clear();
slots.AddRange(node.GetSlots<MaterialSlot>());
if (inputContainer.childCount > 0)
inputContainer.Sort((x, y) => slots.IndexOf(((ShaderPort)x).slot) - slots.IndexOf(((ShaderPort)y).slot));
if (outputContainer.childCount > 0)
outputContainer.Sort((x, y) => slots.IndexOf(((ShaderPort)x).slot) - slots.IndexOf(((ShaderPort)y).slot));
}
}
public bool FindPort(SlotReference slot, out ShaderPort port)
{
port = contentContainer.Q("top")?.Query<ShaderPort>().Where(p => p.slot.slotReference.Equals(slot)).First();
return port != null;
}
public void AttachMessage(string errString, ShaderCompilerMessageSeverity severity)
{
ClearMessage();
IconBadge badge;
badge = IconBadge.CreateComment(errString);
Add(badge);
badge.AttachTo(outputContainer, SpriteAlignment.RightCenter);
}
public void ClearMessage()
{
var badge = this.Q<IconBadge>();
if (badge != null)
{
badge.Detach();
badge.RemoveFromHierarchy();
}
}
public void SetColor(Color newColor)
{
}
public void ResetColor()
{
}
public void UpdateDropdownEntries()
{
}
#endregion
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 04f556529d4fe7c4286efa58a548d86a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,40 @@
using System.Reflection;
using UnityEngine;
namespace UnityEditor.ShaderGraph
{
[Title("Input", "Texture", "Split Texture Transform")]
class SplitTextureTransformNode : CodeFunctionNode
{
public override bool hasPreview { get { return false; } }
public SplitTextureTransformNode()
{
name = "Split Texture Transform";
}
protected override MethodInfo GetFunctionToConvert()
{
return GetType().GetMethod("Unity_SplitTextureTransform", BindingFlags.Static | BindingFlags.NonPublic);
}
static string Unity_SplitTextureTransform(
[Slot(0, Binding.None)] Texture2D In,
[Slot(1, Binding.None)] out Vector2 Tiling,
[Slot(2, Binding.None)] out Vector2 Offset,
[Slot(3, Binding.None)] out Texture2D TextureOnly)
{
TextureOnly = default;
Tiling = default;
Offset = default;
return
@"
{
TextureOnly = In;
TextureOnly.scaleTranslate = float4(1.0f, 1.0f, 0.0f, 0.0f);
Tiling = In.scaleTranslate.xy;
Offset = In.scaleTranslate.zw;
}
";
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 88d9d4ff293792c42a6008e688bbc8fc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,883 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor.Graphing;
using UnityEditor.ShaderGraph.Internal;
namespace UnityEditor.ShaderGraph
{
[HasDependencies(typeof(MinimalSubGraphNode))]
[Title("Utility", "Sub-graph")]
class SubGraphNode : AbstractMaterialNode
, IGeneratesBodyCode
, IOnAssetEnabled
, IGeneratesFunction
, IMayRequireNormal
, IMayRequireTangent
, IMayRequireBitangent
, IMayRequireMeshUV
, IMayRequireScreenPosition
, IMayRequireViewDirection
, IMayRequirePosition
, IMayRequirePositionPredisplacement
, IMayRequireVertexColor
, IMayRequireTime
, IMayRequireFaceSign
, IMayRequireCameraOpaqueTexture
, IMayRequireDepthTexture
, IMayRequireVertexSkinning
, IMayRequireVertexID
{
[Serializable]
public class MinimalSubGraphNode : IHasDependencies
{
[SerializeField]
string m_SerializedSubGraph = string.Empty;
public void GetSourceAssetDependencies(AssetCollection assetCollection)
{
var assetReference = JsonUtility.FromJson<SubGraphAssetReference>(m_SerializedSubGraph);
string guidString = assetReference?.subGraph?.guid;
if (!string.IsNullOrEmpty(guidString) && GUID.TryParse(guidString, out GUID guid))
{
// subgraphs are read as artifacts
// they also should be pulled into .unitypackages
assetCollection.AddAssetDependency(
guid,
AssetCollection.Flags.ArtifactDependency |
AssetCollection.Flags.IsSubGraph |
AssetCollection.Flags.IncludeInExportPackage);
}
}
}
[Serializable]
class SubGraphHelper
{
public SubGraphAsset subGraph;
}
[Serializable]
class SubGraphAssetReference
{
public AssetReference subGraph = default;
public override string ToString()
{
return $"subGraph={subGraph}";
}
}
[Serializable]
class AssetReference
{
public long fileID = default;
public string guid = default;
public int type = default;
public override string ToString()
{
return $"fileID={fileID}, guid={guid}, type={type}";
}
}
[SerializeField]
string m_SerializedSubGraph = string.Empty;
[NonSerialized]
SubGraphAsset m_SubGraph; // This should not be accessed directly by most code -- use the asset property instead, and check for NULL! :)
[SerializeField]
List<string> m_PropertyGuids = new List<string>();
[SerializeField]
List<int> m_PropertyIds = new List<int>();
[SerializeField]
List<string> m_Dropdowns = new List<string>();
[SerializeField]
List<string> m_DropdownSelectedEntries = new List<string>();
public string subGraphGuid
{
get
{
var assetReference = JsonUtility.FromJson<SubGraphAssetReference>(m_SerializedSubGraph);
return assetReference?.subGraph?.guid;
}
}
void LoadSubGraph()
{
if (m_SubGraph == null)
{
if (string.IsNullOrEmpty(m_SerializedSubGraph))
{
return;
}
var graphGuid = subGraphGuid;
var assetPath = AssetDatabase.GUIDToAssetPath(graphGuid);
if (string.IsNullOrEmpty(assetPath))
{
// this happens if the editor has never seen the GUID
// error will be printed by validation code in this case
return;
}
m_SubGraph = AssetDatabase.LoadAssetAtPath<SubGraphAsset>(assetPath);
if (m_SubGraph == null)
{
// this happens if the editor has seen the GUID, but the file has been deleted since then
// error will be printed by validation code in this case
return;
}
m_SubGraph.LoadGraphData();
m_SubGraph.LoadDependencyData();
name = m_SubGraph.name;
}
}
public SubGraphAsset asset
{
get
{
LoadSubGraph();
return m_SubGraph;
}
set
{
if (asset == value)
return;
var helper = new SubGraphHelper();
helper.subGraph = value;
m_SerializedSubGraph = EditorJsonUtility.ToJson(helper, true);
m_SubGraph = null;
UpdateSlots();
Dirty(ModificationScope.Topological);
}
}
public override bool hasPreview
{
get { return true; }
}
public override PreviewMode previewMode
{
get
{
PreviewMode mode = m_PreviewMode;
if ((mode == PreviewMode.Inherit) && (asset != null))
mode = asset.previewMode;
return mode;
}
}
public SubGraphNode()
{
name = "Sub Graph";
}
public override bool allowedInSubGraph
{
get { return true; }
}
public override bool canSetPrecision
{
get { return asset?.subGraphGraphPrecision == GraphPrecision.Graph; }
}
public override void GetInputSlots<T>(MaterialSlot startingSlot, List<T> foundSlots)
{
var allSlots = new List<T>();
GetInputSlots<T>(allSlots);
var info = asset?.GetOutputDependencies(startingSlot.RawDisplayName());
if (info != null)
{
foreach (var slot in allSlots)
{
if (info.ContainsSlot(slot))
foundSlots.Add(slot);
}
}
}
public override void GetOutputSlots<T>(MaterialSlot startingSlot, List<T> foundSlots)
{
var allSlots = new List<T>();
GetOutputSlots<T>(allSlots);
var info = asset?.GetInputDependencies(startingSlot.RawDisplayName());
if (info != null)
{
foreach (var slot in allSlots)
{
if (info.ContainsSlot(slot))
foundSlots.Add(slot);
}
}
}
ShaderStageCapability GetSlotCapability(MaterialSlot slot)
{
SlotDependencyInfo dependencyInfo;
if (slot.isInputSlot)
dependencyInfo = asset?.GetInputDependencies(slot.RawDisplayName());
else
dependencyInfo = asset?.GetOutputDependencies(slot.RawDisplayName());
if (dependencyInfo != null)
return dependencyInfo.capabilities;
return ShaderStageCapability.All;
}
public void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode generationMode)
{
var outputGraphPrecision = asset?.outputGraphPrecision ?? GraphPrecision.Single;
var outputPrecision = outputGraphPrecision.ToConcrete(concretePrecision);
if (asset == null || hasError)
{
var outputSlots = new List<MaterialSlot>();
GetOutputSlots(outputSlots);
foreach (var slot in outputSlots)
{
sb.AppendLine($"{slot.concreteValueType.ToShaderString(outputPrecision)} {GetVariableNameForSlot(slot.id)} = {slot.GetDefaultValue(GenerationMode.ForReals)};");
}
return;
}
var inputVariableName = $"_{GetVariableNameForNode()}";
GenerationUtils.GenerateSurfaceInputTransferCode(sb, asset.requirements, asset.inputStructName, inputVariableName);
// declare output variables
foreach (var outSlot in asset.outputs)
sb.AppendLine("{0} {1};", outSlot.concreteValueType.ToShaderString(outputPrecision), GetVariableNameForSlot(outSlot.id));
var arguments = new List<string>();
foreach (AbstractShaderProperty prop in asset.inputs)
{
// setup the property concrete precision (fallback to node concrete precision when it's switchable)
prop.SetupConcretePrecision(this.concretePrecision);
var inSlotId = m_PropertyIds[m_PropertyGuids.IndexOf(prop.guid.ToString())];
arguments.Add(GetSlotValue(inSlotId, generationMode, prop.concretePrecision));
if (prop.isConnectionTestable)
arguments.Add(IsSlotConnected(inSlotId) ? "true" : "false");
}
var dropdowns = asset.dropdowns;
foreach (var dropdown in dropdowns)
{
var name = GetDropdownEntryName(dropdown.referenceName);
if (dropdown.ContainsEntry(name))
arguments.Add(dropdown.IndexOfName(name).ToString());
else
arguments.Add(dropdown.value.ToString());
}
// pass surface inputs through
arguments.Add(inputVariableName);
foreach (var outSlot in asset.outputs)
arguments.Add(GetVariableNameForSlot(outSlot.id));
foreach (var feedbackSlot in asset.vtFeedbackVariables)
{
string feedbackVar = GetVariableNameForNode() + "_" + feedbackSlot;
sb.AppendLine("{0} {1};", ConcreteSlotValueType.Vector4.ToShaderString(ConcretePrecision.Single), feedbackVar);
arguments.Add(feedbackVar);
}
sb.TryAppendIndentation();
sb.Append(asset.functionName);
sb.Append("(");
bool firstArg = true;
foreach (var arg in arguments)
{
if (!firstArg)
sb.Append(", ");
firstArg = false;
sb.Append(arg);
}
sb.Append(");");
sb.AppendNewLine();
}
public void OnEnable()
{
UpdateSlots();
}
public bool Reload(HashSet<string> changedFileDependencyGUIDs)
{
if (!changedFileDependencyGUIDs.Contains(subGraphGuid))
{
return false;
}
if (asset == null)
{
// asset missing or deleted
return true;
}
if (changedFileDependencyGUIDs.Contains(asset.assetGuid) || asset.descendents.Any(changedFileDependencyGUIDs.Contains))
{
m_SubGraph = null;
UpdateSlots();
if (hasError)
{
return true;
}
owner.ClearErrorsForNode(this);
ValidateNode();
Dirty(ModificationScope.Graph);
}
return true;
}
public override void UpdatePrecision(List<MaterialSlot> inputSlots)
{
if (asset != null)
{
if (asset.subGraphGraphPrecision == GraphPrecision.Graph)
{
// subgraph is defined to be switchable, so use the default behavior to determine precision
base.UpdatePrecision(inputSlots);
}
else
{
// subgraph sets a specific precision, force that
graphPrecision = asset.subGraphGraphPrecision;
concretePrecision = graphPrecision.ToConcrete(owner.graphDefaultConcretePrecision);
}
}
else
{
// no subgraph asset; use default behavior
base.UpdatePrecision(inputSlots);
}
}
public virtual void UpdateSlots()
{
var validNames = new List<int>();
if (asset == null)
{
return;
}
var props = asset.inputs;
var toFix = new HashSet<(SlotReference from, SlotReference to)>();
foreach (var prop in props)
{
SlotValueType valueType = prop.concreteShaderValueType.ToSlotValueType();
var propertyString = prop.guid.ToString();
var propertyIndex = m_PropertyGuids.IndexOf(propertyString);
if (propertyIndex < 0)
{
propertyIndex = m_PropertyGuids.Count;
m_PropertyGuids.Add(propertyString);
m_PropertyIds.Add(prop.guid.GetHashCode());
}
var id = m_PropertyIds[propertyIndex];
//for whatever reason, it seems like shader property ids changed between 21.2a17 and 21.2b1
//tried tracking it down, couldnt find any reason for it, so we gotta fix it in post (after we deserialize)
List<MaterialSlot> inputs = new List<MaterialSlot>();
MaterialSlot found = null;
GetInputSlots(inputs);
foreach (var input in inputs)
{
if (input.shaderOutputName == prop.referenceName && input.id != id)
{
found = input;
break;
}
}
MaterialSlot slot = MaterialSlot.CreateMaterialSlot(valueType, id, prop.displayName, prop.referenceName, SlotType.Input, Vector4.zero, ShaderStageCapability.All);
// Copy defaults
switch (prop.concreteShaderValueType)
{
case ConcreteSlotValueType.SamplerState:
{
var tSlot = slot as SamplerStateMaterialSlot;
var tProp = prop as SamplerStateShaderProperty;
if (tSlot != null && tProp != null)
tSlot.defaultSamplerState = tProp.value;
}
break;
case ConcreteSlotValueType.Matrix4:
{
var tSlot = slot as Matrix4MaterialSlot;
var tProp = prop as Matrix4ShaderProperty;
if (tSlot != null && tProp != null)
tSlot.value = tProp.value;
}
break;
case ConcreteSlotValueType.Matrix3:
{
var tSlot = slot as Matrix3MaterialSlot;
var tProp = prop as Matrix3ShaderProperty;
if (tSlot != null && tProp != null)
tSlot.value = tProp.value;
}
break;
case ConcreteSlotValueType.Matrix2:
{
var tSlot = slot as Matrix2MaterialSlot;
var tProp = prop as Matrix2ShaderProperty;
if (tSlot != null && tProp != null)
tSlot.value = tProp.value;
}
break;
case ConcreteSlotValueType.Texture2D:
{
var tSlot = slot as Texture2DInputMaterialSlot;
var tProp = prop as Texture2DShaderProperty;
if (tSlot != null && tProp != null)
tSlot.texture = tProp.value.texture;
}
break;
case ConcreteSlotValueType.Texture2DArray:
{
var tSlot = slot as Texture2DArrayInputMaterialSlot;
var tProp = prop as Texture2DArrayShaderProperty;
if (tSlot != null && tProp != null)
tSlot.textureArray = tProp.value.textureArray;
}
break;
case ConcreteSlotValueType.Texture3D:
{
var tSlot = slot as Texture3DInputMaterialSlot;
var tProp = prop as Texture3DShaderProperty;
if (tSlot != null && tProp != null)
tSlot.texture = tProp.value.texture;
}
break;
case ConcreteSlotValueType.Cubemap:
{
var tSlot = slot as CubemapInputMaterialSlot;
var tProp = prop as CubemapShaderProperty;
if (tSlot != null && tProp != null)
tSlot.cubemap = tProp.value.cubemap;
}
break;
case ConcreteSlotValueType.Gradient:
{
var tSlot = slot as GradientInputMaterialSlot;
var tProp = prop as GradientShaderProperty;
if (tSlot != null && tProp != null)
tSlot.value = tProp.value;
}
break;
case ConcreteSlotValueType.Vector4:
{
var tSlot = slot as Vector4MaterialSlot;
var vector4Prop = prop as Vector4ShaderProperty;
var colorProp = prop as ColorShaderProperty;
if (tSlot != null && vector4Prop != null)
tSlot.value = vector4Prop.value;
else if (tSlot != null && colorProp != null)
tSlot.value = colorProp.value;
}
break;
case ConcreteSlotValueType.Vector3:
{
var tSlot = slot as Vector3MaterialSlot;
var tProp = prop as Vector3ShaderProperty;
if (tSlot != null && tProp != null)
tSlot.value = tProp.value;
}
break;
case ConcreteSlotValueType.Vector2:
{
var tSlot = slot as Vector2MaterialSlot;
var tProp = prop as Vector2ShaderProperty;
if (tSlot != null && tProp != null)
tSlot.value = tProp.value;
}
break;
case ConcreteSlotValueType.Vector1:
{
var tSlot = slot as Vector1MaterialSlot;
var tProp = prop as Vector1ShaderProperty;
if (tSlot != null && tProp != null)
tSlot.value = tProp.value;
}
break;
case ConcreteSlotValueType.Boolean:
{
var tSlot = slot as BooleanMaterialSlot;
var tProp = prop as BooleanShaderProperty;
if (tSlot != null && tProp != null)
tSlot.value = tProp.value;
}
break;
}
AddSlot(slot);
validNames.Add(id);
if (found != null)
{
List<IEdge> edges = new List<IEdge>();
owner.GetEdges(found.slotReference, edges);
foreach (var edge in edges)
{
toFix.Add((edge.outputSlot, slot.slotReference));
}
}
}
foreach (var slot in asset.outputs)
{
var outputStage = GetSlotCapability(slot);
var newSlot = MaterialSlot.CreateMaterialSlot(slot.valueType, slot.id, slot.RawDisplayName(),
slot.shaderOutputName, SlotType.Output, Vector4.zero, outputStage, slot.hidden);
AddSlot(newSlot);
validNames.Add(slot.id);
}
RemoveSlotsNameNotMatching(validNames, true);
// sort slot order to match subgraph property order
SetSlotOrder(validNames);
foreach (var (from, to) in toFix)
{
//for whatever reason, in this particular error fix, GraphView will incorrectly either add two edgeViews or none
//but it does work correctly if we dont notify GraphView of this added edge. Gross.
owner.UnnotifyAddedEdge(owner.Connect(from, to));
}
}
void ValidateShaderStage()
{
if (asset != null)
{
List<MaterialSlot> slots = new List<MaterialSlot>();
GetInputSlots(slots);
GetOutputSlots(slots);
foreach (MaterialSlot slot in slots)
slot.stageCapability = GetSlotCapability(slot);
}
}
public override void ValidateNode()
{
base.ValidateNode();
if (asset == null)
{
hasError = true;
var assetGuid = subGraphGuid;
var assetPath = string.IsNullOrEmpty(subGraphGuid) ? null : AssetDatabase.GUIDToAssetPath(assetGuid);
if (string.IsNullOrEmpty(assetPath))
{
owner.AddValidationError(objectId, $"Could not find Sub Graph asset with GUID {assetGuid}.");
}
else
{
owner.AddValidationError(objectId, $"Could not load Sub Graph asset at \"{assetPath}\" with GUID {assetGuid}.");
}
return;
}
if (owner.isSubGraph && (asset.descendents.Contains(owner.assetGuid) || asset.assetGuid == owner.assetGuid))
{
hasError = true;
owner.AddValidationError(objectId, $"Detected a recursion in Sub Graph asset at \"{AssetDatabase.GUIDToAssetPath(subGraphGuid)}\" with GUID {subGraphGuid}.");
}
else if (!asset.isValid)
{
hasError = true;
owner.AddValidationError(objectId, $"Sub Graph has errors, asset at \"{AssetDatabase.GUIDToAssetPath(subGraphGuid)}\" with GUID {subGraphGuid}.");
}
else if (!owner.isSubGraph && owner.activeTargets.Any(x => asset.unsupportedTargets.Contains(x)))
{
SetOverrideActiveState(ActiveState.ExplicitInactive);
owner.AddValidationError(objectId, $"Sub Graph contains nodes that are unsupported by the current active targets, asset at \"{AssetDatabase.GUIDToAssetPath(subGraphGuid)}\" with GUID {subGraphGuid}.");
}
// detect disconnected VT properties, and VT layer count mismatches
foreach (var paramProp in asset.inputs)
{
if (paramProp is VirtualTextureShaderProperty vtProp)
{
int paramLayerCount = vtProp.value.layers.Count;
var argSlotId = m_PropertyIds[m_PropertyGuids.IndexOf(paramProp.guid.ToString())]; // yikes
if (!IsSlotConnected(argSlotId))
{
owner.AddValidationError(objectId, $"A VirtualTexture property must be connected to the input slot \"{paramProp.displayName}\"");
}
else
{
var argProp = GetSlotProperty(argSlotId) as VirtualTextureShaderProperty;
if (argProp != null)
{
int argLayerCount = argProp.value.layers.Count;
if (argLayerCount != paramLayerCount)
owner.AddValidationError(objectId, $"Input \"{paramProp.displayName}\" has different number of layers from the connected property \"{argProp.displayName}\"");
}
else
{
owner.AddValidationError(objectId, $"Input \"{paramProp.displayName}\" is not connected to a valid VirtualTexture property");
}
}
break;
}
}
ValidateShaderStage();
}
public override void CollectShaderProperties(PropertyCollector visitor, GenerationMode generationMode)
{
base.CollectShaderProperties(visitor, generationMode);
if (asset == null)
return;
foreach (var property in asset.nodeProperties)
{
visitor.AddShaderProperty(property);
}
}
public AbstractShaderProperty GetShaderProperty(int id)
{
var index = m_PropertyIds.IndexOf(id);
if (index >= 0)
{
var guid = m_PropertyGuids[index];
return asset?.inputs.Where(x => x.guid.ToString().Equals(guid)).FirstOrDefault();
}
return null;
}
public void CollectShaderKeywords(KeywordCollector keywords, GenerationMode generationMode)
{
if (asset == null)
return;
foreach (var keyword in asset.keywords)
{
keywords.AddShaderKeyword(keyword as ShaderKeyword);
}
}
public override void CollectPreviewMaterialProperties(List<PreviewProperty> properties)
{
base.CollectPreviewMaterialProperties(properties);
if (asset == null)
return;
foreach (var property in asset.nodeProperties)
{
properties.Add(property.GetPreviewMaterialProperty());
}
}
public virtual void GenerateNodeFunction(FunctionRegistry registry, GenerationMode generationMode)
{
if (asset == null || hasError)
return;
registry.RequiresIncludes(asset.includes);
var graphData = registry.builder.currentNode.owner;
var graphDefaultConcretePrecision = graphData.graphDefaultConcretePrecision;
foreach (var function in asset.functions)
{
var name = function.key;
var source = function.value;
var graphPrecisionFlags = function.graphPrecisionFlags;
// the subgraph may use multiple precision variants of this function internally
// here we iterate through all the requested precisions and forward those requests out to the graph
for (int requestedGraphPrecision = 0; requestedGraphPrecision <= (int)GraphPrecision.Half; requestedGraphPrecision++)
{
// only provide requested precisions
if ((graphPrecisionFlags & (1 << requestedGraphPrecision)) != 0)
{
// when a function coming from a subgraph asset has a graph precision of "Graph",
// that means it is up to the subgraph NODE to decide (i.e. us!)
GraphPrecision actualGraphPrecision = (GraphPrecision)requestedGraphPrecision;
// subgraph asset setting falls back to this node setting (when switchable)
actualGraphPrecision = actualGraphPrecision.GraphFallback(this.graphPrecision);
// which falls back to the graph default concrete precision
ConcretePrecision actualConcretePrecision = actualGraphPrecision.ToConcrete(graphDefaultConcretePrecision);
// forward the function into the current graph
registry.ProvideFunction(name, actualGraphPrecision, actualConcretePrecision, sb => sb.AppendLines(source));
}
}
}
}
public NeededCoordinateSpace RequiresNormal(ShaderStageCapability stageCapability)
{
if (asset == null)
return NeededCoordinateSpace.None;
return asset.requirements.requiresNormal;
}
public bool RequiresMeshUV(UVChannel channel, ShaderStageCapability stageCapability)
{
if (asset == null)
return false;
return asset.requirements.requiresMeshUVs.Contains(channel);
}
public bool RequiresScreenPosition(ShaderStageCapability stageCapability)
{
if (asset == null)
return false;
return asset.requirements.requiresScreenPosition;
}
public NeededCoordinateSpace RequiresViewDirection(ShaderStageCapability stageCapability)
{
if (asset == null)
return NeededCoordinateSpace.None;
return asset.requirements.requiresViewDir;
}
public NeededCoordinateSpace RequiresPosition(ShaderStageCapability stageCapability)
{
if (asset == null)
return NeededCoordinateSpace.None;
return asset.requirements.requiresPosition;
}
public NeededCoordinateSpace RequiresPositionPredisplacement(ShaderStageCapability stageCapability = ShaderStageCapability.All)
{
if (asset == null)
return NeededCoordinateSpace.None;
return asset.requirements.requiresPositionPredisplacement;
}
public NeededCoordinateSpace RequiresTangent(ShaderStageCapability stageCapability)
{
if (asset == null)
return NeededCoordinateSpace.None;
return asset.requirements.requiresTangent;
}
public bool RequiresTime()
{
if (asset == null)
return false;
return asset.requirements.requiresTime;
}
public bool RequiresFaceSign(ShaderStageCapability stageCapability)
{
if (asset == null)
return false;
return asset.requirements.requiresFaceSign;
}
public NeededCoordinateSpace RequiresBitangent(ShaderStageCapability stageCapability)
{
if (asset == null)
return NeededCoordinateSpace.None;
return asset.requirements.requiresBitangent;
}
public bool RequiresVertexColor(ShaderStageCapability stageCapability)
{
if (asset == null)
return false;
return asset.requirements.requiresVertexColor;
}
public bool RequiresCameraOpaqueTexture(ShaderStageCapability stageCapability)
{
if (asset == null)
return false;
return asset.requirements.requiresCameraOpaqueTexture;
}
public bool RequiresDepthTexture(ShaderStageCapability stageCapability)
{
if (asset == null)
return false;
return asset.requirements.requiresDepthTexture;
}
public bool RequiresVertexSkinning(ShaderStageCapability stageCapability)
{
if (asset == null)
return false;
return asset.requirements.requiresVertexSkinning;
}
public bool RequiresVertexID(ShaderStageCapability stageCapability)
{
if (asset == null)
return false;
return asset.requirements.requiresVertexID;
}
public string GetDropdownEntryName(string referenceName)
{
var index = m_Dropdowns.IndexOf(referenceName);
return index >= 0 ? m_DropdownSelectedEntries[index] : string.Empty;
}
public void SetDropdownEntryName(string referenceName, string value)
{
var index = m_Dropdowns.IndexOf(referenceName);
if (index >= 0)
{
m_DropdownSelectedEntries[index] = value;
}
else
{
m_Dropdowns.Add(referenceName);
m_DropdownSelectedEntries.Add(value);
}
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 4967c0a2468b2c5438ff10f9f8e84f26
timeCreated: 1465559218
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: