initial commit
This commit is contained in:
parent
6715289efe
commit
788c3389af
37645 changed files with 2526849 additions and 80 deletions
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fe9ce2929aeb84ff4aca41505b173cec
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ec74026c92381d24bbc45e040822e702
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 06a79a4c3d9dfd245a9c817c1fd44d20
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1c5103f75b5ec7445bb4c7b5a36f6aec
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: aa03a542537ab4fb4a01a004efa97f58
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
|
@ -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;
|
||||
}
|
||||
";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 34929a6d0bb40407fb00206ab0a10fa1
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
|
@ -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);
|
||||
}
|
||||
";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f3d072ddeeee848848f2e59dbb5cf1d9
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
|
@ -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;
|
||||
}
|
||||
";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 15ad3dc0ff22d7748af5e82ef5503d32
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
|
@ -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};");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: be7845eb62257564492a5c3ba1d0d181
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c66cc68d0d0862b4c8ddfc00093d0ae0
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c7e3dd61523f54e52ade29765e299d3f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6eaf1bad910084b85ac9ead4319c7820
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
|
@ -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;
|
||||
}
|
||||
";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0e15b8359c9db4d0ea27f4deab94137e
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
|
@ -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;
|
||||
}
|
||||
";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4defde87a9d5e44d8b8900d3f8ef6440
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
|
@ -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;
|
||||
}
|
||||
";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: acb4138f9cef644dfa59102e871d1820
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
|
@ -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;
|
||||
}
|
||||
";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0f6f860c0f1e24214b5d2974cc624227
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
|
@ -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;
|
||||
}
|
||||
";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0a9c3dd2c3b0c4347876f3e448be2046
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b70493ee47ef82b46be4fe3568b0bb2d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 04f556529d4fe7c4286efa58a548d86a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 88d9d4ff293792c42a6008e688bbc8fc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4967c0a2468b2c5438ff10f9f8e84f26
|
||||
timeCreated: 1465559218
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Add table
Add a link
Reference in a new issue