Boat Attack使用了Universal RP的许多新图形功能,可以用于探索 Universal RP 的使用方式和技巧。
您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 

737 行
25 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor.Graphing;
using UnityEditor.ShaderGraph.Drawing.Colors;
using UnityEditor.ShaderGraph.Internal;
namespace UnityEditor.ShaderGraph
{
[Serializable]
abstract class AbstractMaterialNode : ISerializationCallbackReceiver, IGroupItem
{
protected static List<MaterialSlot> s_TempSlots = new List<MaterialSlot>();
protected static List<IEdge> s_TempEdges = new List<IEdge>();
protected static List<PreviewProperty> s_TempPreviewProperties = new List<PreviewProperty>();
[NonSerialized]
private Guid m_Guid;
[SerializeField]
private string m_GuidSerialized;
[NonSerialized]
Guid m_GroupGuid;
[SerializeField]
string m_GroupGuidSerialized;
[SerializeField]
private string m_Name;
[SerializeField]
protected int m_NodeVersion;
[SerializeField]
private DrawState m_DrawState;
[NonSerialized]
bool m_HasError;
[NonSerialized]
private List<ISlot> m_Slots = new List<ISlot>();
[SerializeField]
List<SerializationHelper.JSONSerializedElement> m_SerializableSlots = new List<SerializationHelper.JSONSerializedElement>();
public Identifier tempId { get; set; }
public GraphData owner { get; set; }
OnNodeModified m_OnModified;
public void RegisterCallback(OnNodeModified callback)
{
m_OnModified += callback;
}
public void UnregisterCallback(OnNodeModified callback)
{
m_OnModified -= callback;
}
public void Dirty(ModificationScope scope)
{
if (m_OnModified != null)
m_OnModified(this, scope);
}
public Guid guid
{
get { return m_Guid; }
}
public Guid groupGuid
{
get { return m_GroupGuid; }
set { m_GroupGuid = value; }
}
public string name
{
get { return m_Name; }
set { m_Name = value; }
}
public virtual string documentationURL => NodeUtils.GetDocumentationString(this);
public virtual bool canDeleteNode
{
get { return owner != null && guid != owner.activeOutputNodeGuid; }
}
public DrawState drawState
{
get { return m_DrawState; }
set
{
m_DrawState = value;
Dirty(ModificationScope.Layout);
}
}
public virtual bool canSetPrecision
{
get { return true; }
}
private ConcretePrecision m_ConcretePrecision = ConcretePrecision.Float;
public ConcretePrecision concretePrecision
{
get => m_ConcretePrecision;
set => m_ConcretePrecision = value;
}
[SerializeField]
private Precision m_Precision = Precision.Inherit;
public Precision precision
{
get => m_Precision;
set => m_Precision = value;
}
[SerializeField]
bool m_PreviewExpanded = true;
public bool previewExpanded
{
get { return m_PreviewExpanded; }
set
{
if (previewExpanded == value)
return;
m_PreviewExpanded = value;
Dirty(ModificationScope.Node);
}
}
// Nodes that want to have a preview area can override this and return true
public virtual bool hasPreview
{
get { return false; }
}
public virtual PreviewMode previewMode
{
get { return PreviewMode.Preview2D; }
}
public virtual bool allowedInSubGraph
{
get { return true; }
}
public virtual bool allowedInMainGraph
{
get { return true; }
}
public virtual bool allowedInLayerGraph
{
get { return true; }
}
public virtual bool hasError
{
get { return m_HasError; }
protected set { m_HasError = value; }
}
string m_DefaultVariableName;
string m_NameForDefaultVariableName;
Guid m_GuidForDefaultVariableName;
string defaultVariableName
{
get
{
if (m_NameForDefaultVariableName != name || m_GuidForDefaultVariableName != guid)
{
m_DefaultVariableName = string.Format("{0}_{1}", NodeUtils.GetHLSLSafeName(name ?? "node"), GuidEncoder.Encode(guid));
m_NameForDefaultVariableName = name;
m_GuidForDefaultVariableName = guid;
}
return m_DefaultVariableName;
}
}
#region Custom Colors
[SerializeField]
CustomColorData m_CustomColors = new CustomColorData();
public bool TryGetColor(string provider, ref Color color)
{
return m_CustomColors.TryGetColor(provider, out color);
}
public void ResetColor(string provider)
{
m_CustomColors.Remove(provider);
}
public void SetColor(string provider, Color color)
{
m_CustomColors.Set(provider, color);
}
#endregion
protected AbstractMaterialNode()
{
m_DrawState.expanded = true;
m_Guid = Guid.NewGuid();
version = 0;
}
public Guid RewriteGuid()
{
m_Guid = Guid.NewGuid();
return m_Guid;
}
public void GetInputSlots<T>(List<T> foundSlots) where T : ISlot
{
foreach (var slot in m_Slots)
{
if (slot.isInputSlot && slot is T)
foundSlots.Add((T)slot);
}
}
public void GetOutputSlots<T>(List<T> foundSlots) where T : ISlot
{
foreach (var slot in m_Slots)
{
if (slot.isOutputSlot && slot is T)
foundSlots.Add((T)slot);
}
}
public void GetSlots<T>(List<T> foundSlots) where T : ISlot
{
foreach (var slot in m_Slots)
{
if (slot is T)
foundSlots.Add((T)slot);
}
}
public virtual void CollectShaderProperties(PropertyCollector properties, GenerationMode generationMode)
{
foreach (var inputSlot in this.GetInputSlots<MaterialSlot>())
{
var edges = owner.GetEdges(inputSlot.slotReference);
if (edges.Any())
continue;
inputSlot.AddDefaultProperty(properties, generationMode);
}
}
public string GetSlotValue(int inputSlotId, GenerationMode generationMode, ConcretePrecision concretePrecision)
{
string slotValue = GetSlotValue(inputSlotId, generationMode);
return slotValue.Replace(PrecisionUtil.Token, concretePrecision.ToShaderString());
}
public string GetSlotValue(int inputSlotId, GenerationMode generationMode)
{
var inputSlot = FindSlot<MaterialSlot>(inputSlotId);
if (inputSlot == null)
return string.Empty;
var edges = owner.GetEdges(inputSlot.slotReference).ToArray();
if (edges.Any())
{
var fromSocketRef = edges[0].outputSlot;
var fromNode = owner.GetNodeFromGuid<AbstractMaterialNode>(fromSocketRef.nodeGuid);
if (fromNode == null)
return string.Empty;
var slot = fromNode.FindOutputSlot<MaterialSlot>(fromSocketRef.slotId);
if (slot == null)
return string.Empty;
return ShaderGenerator.AdaptNodeOutput(fromNode, slot.id, inputSlot.concreteValueType);
}
return inputSlot.GetDefaultValue(generationMode);
}
public static ConcreteSlotValueType ConvertDynamicVectorInputTypeToConcrete(IEnumerable<ConcreteSlotValueType> inputTypes)
{
var concreteSlotValueTypes = inputTypes as IList<ConcreteSlotValueType> ?? inputTypes.ToList();
var inputTypesDistinct = concreteSlotValueTypes.Distinct().ToList();
switch (inputTypesDistinct.Count)
{
case 0:
return ConcreteSlotValueType.Vector1;
case 1:
if(SlotValueHelper.AreCompatible(SlotValueType.DynamicVector, inputTypesDistinct.First()))
return inputTypesDistinct.First();
break;
default:
// find the 'minumum' channel width excluding 1 as it can promote
inputTypesDistinct.RemoveAll(x => x == ConcreteSlotValueType.Vector1);
var ordered = inputTypesDistinct.OrderByDescending(x => x);
if (ordered.Any())
return ordered.FirstOrDefault();
break;
}
return ConcreteSlotValueType.Vector1;
}
public static ConcreteSlotValueType ConvertDynamicMatrixInputTypeToConcrete(IEnumerable<ConcreteSlotValueType> inputTypes)
{
var concreteSlotValueTypes = inputTypes as IList<ConcreteSlotValueType> ?? inputTypes.ToList();
var inputTypesDistinct = concreteSlotValueTypes.Distinct().ToList();
switch (inputTypesDistinct.Count)
{
case 0:
return ConcreteSlotValueType.Matrix2;
case 1:
return inputTypesDistinct.FirstOrDefault();
default:
var ordered = inputTypesDistinct.OrderByDescending(x => x);
if (ordered.Any())
return ordered.FirstOrDefault();
break;
}
return ConcreteSlotValueType.Matrix2;
}
protected const string k_validationErrorMessage = "Error found during node validation";
public virtual bool ValidateConcretePrecision(ref string errorMessage)
{
// If Node has a precision override use that
if (precision != Precision.Inherit)
{
m_ConcretePrecision = precision.ToConcrete();
return false;
}
// Get inputs
s_TempSlots.Clear();
GetInputSlots(s_TempSlots);
// If no inputs were found use the precision of the Graph
// This can be removed when parameters are considered as true inputs
if (s_TempSlots.Count == 0)
{
m_ConcretePrecision = owner.concretePrecision;
return false;
}
// Otherwise compare precisions from inputs
var precisionsToCompare = new List<int>();
bool isInError = false;
foreach (var inputSlot in s_TempSlots)
{
// If input port doesnt have an edge use the Graph's precision for that input
var edges = owner.GetEdges(inputSlot.slotReference).ToList();
if (!edges.Any())
{
precisionsToCompare.Add((int)owner.concretePrecision);
continue;
}
// Get output node from edge
var outputSlotRef = edges[0].outputSlot;
var outputNode = owner.GetNodeFromGuid(outputSlotRef.nodeGuid);
if (outputNode == null)
{
errorMessage = string.Format("Failed to find Node with Guid {0}", outputSlotRef.nodeGuid);
isInError = true;
continue;
}
// Use precision from connected Node
precisionsToCompare.Add((int)outputNode.concretePrecision);
}
// Use highest precision from all input sources
m_ConcretePrecision = (ConcretePrecision)precisionsToCompare.OrderBy(x => x).First();
// Clean up
s_TempSlots.Clear();
return isInError;
}
public virtual void ValidateNode()
{
var isInError = false;
var errorMessage = k_validationErrorMessage;
var dynamicInputSlotsToCompare = DictionaryPool<DynamicVectorMaterialSlot, ConcreteSlotValueType>.Get();
var skippedDynamicSlots = ListPool<DynamicVectorMaterialSlot>.Get();
var dynamicMatrixInputSlotsToCompare = DictionaryPool<DynamicMatrixMaterialSlot, ConcreteSlotValueType>.Get();
var skippedDynamicMatrixSlots = ListPool<DynamicMatrixMaterialSlot>.Get();
// iterate the input slots
s_TempSlots.Clear();
GetInputSlots(s_TempSlots);
foreach (var inputSlot in s_TempSlots)
{
inputSlot.hasError = false;
// if there is a connection
var edges = owner.GetEdges(inputSlot.slotReference).ToList();
if (!edges.Any())
{
if (inputSlot is DynamicVectorMaterialSlot)
skippedDynamicSlots.Add(inputSlot as DynamicVectorMaterialSlot);
if (inputSlot is DynamicMatrixMaterialSlot)
skippedDynamicMatrixSlots.Add(inputSlot as DynamicMatrixMaterialSlot);
continue;
}
// get the output details
var outputSlotRef = edges[0].outputSlot;
var outputNode = owner.GetNodeFromGuid(outputSlotRef.nodeGuid);
if (outputNode == null)
continue;
var outputSlot = outputNode.FindOutputSlot<MaterialSlot>(outputSlotRef.slotId);
if (outputSlot == null)
continue;
if (outputSlot.hasError)
{
inputSlot.hasError = true;
continue;
}
var outputConcreteType = outputSlot.concreteValueType;
// dynamic input... depends on output from other node.
// we need to compare ALL dynamic inputs to make sure they
// are compatible.
if (inputSlot is DynamicVectorMaterialSlot)
{
dynamicInputSlotsToCompare.Add((DynamicVectorMaterialSlot)inputSlot, outputConcreteType);
continue;
}
else if (inputSlot is DynamicMatrixMaterialSlot)
{
dynamicMatrixInputSlotsToCompare.Add((DynamicMatrixMaterialSlot)inputSlot, outputConcreteType);
continue;
}
}
// we can now figure out the dynamic slotType
// from here set all the
var dynamicType = ConvertDynamicVectorInputTypeToConcrete(dynamicInputSlotsToCompare.Values);
foreach (var dynamicKvP in dynamicInputSlotsToCompare)
dynamicKvP.Key.SetConcreteType(dynamicType);
foreach (var skippedSlot in skippedDynamicSlots)
skippedSlot.SetConcreteType(dynamicType);
// and now dynamic matrices
var dynamicMatrixType = ConvertDynamicMatrixInputTypeToConcrete(dynamicMatrixInputSlotsToCompare.Values);
foreach (var dynamicKvP in dynamicMatrixInputSlotsToCompare)
dynamicKvP.Key.SetConcreteType(dynamicMatrixType);
foreach (var skippedSlot in skippedDynamicMatrixSlots)
skippedSlot.SetConcreteType(dynamicMatrixType);
s_TempSlots.Clear();
GetInputSlots(s_TempSlots);
var inputError = s_TempSlots.Any(x => x.hasError);
// configure the output slots now
// their slotType will either be the default output slotType
// or the above dynamic slotType for dynamic nodes
// or error if there is an input error
s_TempSlots.Clear();
GetOutputSlots(s_TempSlots);
foreach (var outputSlot in s_TempSlots)
{
outputSlot.hasError = false;
if (inputError)
{
outputSlot.hasError = true;
continue;
}
if (outputSlot is DynamicVectorMaterialSlot)
{
(outputSlot as DynamicVectorMaterialSlot).SetConcreteType(dynamicType);
continue;
}
else if (outputSlot is DynamicMatrixMaterialSlot)
{
(outputSlot as DynamicMatrixMaterialSlot).SetConcreteType(dynamicMatrixType);
continue;
}
}
isInError |= inputError;
s_TempSlots.Clear();
GetOutputSlots(s_TempSlots);
isInError |= s_TempSlots.Any(x => x.hasError);
isInError |= CalculateNodeHasError(ref errorMessage);
isInError |= ValidateConcretePrecision(ref errorMessage);
hasError = isInError;
if (isInError)
{
((GraphData) owner).AddValidationError(tempId, errorMessage);
}
else
{
++version;
}
ListPool<DynamicVectorMaterialSlot>.Release(skippedDynamicSlots);
DictionaryPool<DynamicVectorMaterialSlot, ConcreteSlotValueType>.Release(dynamicInputSlotsToCompare);
ListPool<DynamicMatrixMaterialSlot>.Release(skippedDynamicMatrixSlots);
DictionaryPool<DynamicMatrixMaterialSlot, ConcreteSlotValueType>.Release(dynamicMatrixInputSlotsToCompare);
}
public int version { get; set; }
public virtual bool canCopyNode => true;
//True if error
protected virtual bool CalculateNodeHasError(ref string errorMessage)
{
foreach (var slot in this.GetInputSlots<MaterialSlot>())
{
if (slot.isConnected)
{
var edge = owner.GetEdges(slot.slotReference).First();
var outputNode = owner.GetNodeFromGuid(edge.outputSlot.nodeGuid);
var outputSlot = outputNode.GetOutputSlots<MaterialSlot>().First(s => s.id == edge.outputSlot.slotId);
if (!slot.IsCompatibleWith(outputSlot))
{
errorMessage = $"Slot {slot.RawDisplayName()} cannot accept input of type {outputSlot.concreteValueType}.";
return true;
}
}
}
return false;
}
public virtual void CollectPreviewMaterialProperties(List<PreviewProperty> properties)
{
s_TempSlots.Clear();
GetInputSlots(s_TempSlots);
foreach (var s in s_TempSlots)
{
s_TempPreviewProperties.Clear();
s_TempEdges.Clear();
owner.GetEdges(s.slotReference, s_TempEdges);
if (s_TempEdges.Any())
continue;
s.GetPreviewProperties(s_TempPreviewProperties, GetVariableNameForSlot(s.id));
for (int i = 0; i < s_TempPreviewProperties.Count; i++)
{
if (s_TempPreviewProperties[i].name == null)
continue;
properties.Add(s_TempPreviewProperties[i]);
}
}
}
public virtual string GetVariableNameForSlot(int slotId)
{
var slot = FindSlot<MaterialSlot>(slotId);
if (slot == null)
throw new ArgumentException(string.Format("Attempting to use MaterialSlot({0}) on node of type {1} where this slot can not be found", slotId, this), "slotId");
return string.Format("_{0}_{1}_{2}", GetVariableNameForNode(), NodeUtils.GetHLSLSafeName(slot.shaderOutputName), unchecked((uint)slotId));
}
public virtual string GetVariableNameForNode()
{
return defaultVariableName;
}
public void AddSlot(ISlot slot)
{
if (!(slot is MaterialSlot))
throw new ArgumentException(string.Format("Trying to add slot {0} to Material node {1}, but it is not a {2}", slot, this, typeof(MaterialSlot)));
var addingSlot = (MaterialSlot)slot;
var foundSlot = FindSlot<MaterialSlot>(slot.id);
// this will remove the old slot and add a new one
// if an old one was found. This allows updating values
m_Slots.RemoveAll(x => x.id == slot.id);
m_Slots.Add(slot);
slot.owner = this;
OnSlotsChanged();
if (foundSlot == null)
return;
addingSlot.CopyValuesFrom(foundSlot);
}
public void RemoveSlot(int slotId)
{
// Remove edges that use this slot
// no owner can happen after creation
// but before added to graph
if (owner != null)
{
var edges = owner.GetEdges(GetSlotReference(slotId));
foreach (var edge in edges.ToArray())
owner.RemoveEdge(edge);
}
//remove slots
m_Slots.RemoveAll(x => x.id == slotId);
OnSlotsChanged();
}
protected virtual void OnSlotsChanged()
{
Dirty(ModificationScope.Topological);
owner?.ClearErrorsForNode(this);
}
public void RemoveSlotsNameNotMatching(IEnumerable<int> slotIds, bool supressWarnings = false)
{
var invalidSlots = m_Slots.Select(x => x.id).Except(slotIds);
foreach (var invalidSlot in invalidSlots.ToArray())
{
if (!supressWarnings)
Debug.LogWarningFormat("Removing Invalid MaterialSlot: {0}", invalidSlot);
RemoveSlot(invalidSlot);
}
}
public SlotReference GetSlotReference(int slotId)
{
var slot = FindSlot<ISlot>(slotId);
if (slot == null)
throw new ArgumentException("Slot could not be found", "slotId");
return new SlotReference(guid, slotId);
}
public T FindSlot<T>(int slotId) where T : ISlot
{
foreach (var slot in m_Slots)
{
if (slot.id == slotId && slot is T)
return (T)slot;
}
return default(T);
}
public T FindInputSlot<T>(int slotId) where T : ISlot
{
foreach (var slot in m_Slots)
{
if (slot.isInputSlot && slot.id == slotId && slot is T)
return (T)slot;
}
return default(T);
}
public T FindOutputSlot<T>(int slotId) where T : ISlot
{
foreach (var slot in m_Slots)
{
if (slot.isOutputSlot && slot.id == slotId && slot is T)
return (T)slot;
}
return default(T);
}
public virtual IEnumerable<ISlot> GetInputsWithNoConnection()
{
return this.GetInputSlots<ISlot>().Where(x => !owner.GetEdges(GetSlotReference(x.id)).Any());
}
public virtual void OnBeforeSerialize()
{
m_GuidSerialized = m_Guid.ToString();
m_GroupGuidSerialized = m_GroupGuid.ToString();
m_SerializableSlots = SerializationHelper.Serialize<ISlot>(m_Slots);
}
public virtual void OnAfterDeserialize()
{
if (!string.IsNullOrEmpty(m_GuidSerialized))
m_Guid = new Guid(m_GuidSerialized);
else
m_Guid = Guid.NewGuid();
if (m_NodeVersion != GetCompiledNodeVersion())
{
UpgradeNodeWithVersion(m_NodeVersion, GetCompiledNodeVersion());
m_NodeVersion = GetCompiledNodeVersion();
}
if (!string.IsNullOrEmpty(m_GroupGuidSerialized))
m_GroupGuid = new Guid(m_GroupGuidSerialized);
else
m_GroupGuid = Guid.Empty;
m_Slots = SerializationHelper.Deserialize<ISlot>(m_SerializableSlots, GraphUtil.GetLegacyTypeRemapping());
m_SerializableSlots = null;
foreach (var s in m_Slots)
s.owner = this;
UpdateNodeAfterDeserialization();
}
public virtual void UpdateNodeAfterDeserialization()
{}
public virtual int GetCompiledNodeVersion() => 0;
public virtual void UpgradeNodeWithVersion(int from, int to)
{}
public bool IsSlotConnected(int slotId)
{
var slot = FindSlot<MaterialSlot>(slotId);
return slot != null && owner.GetEdges(slot.slotReference).Any();
}
}
}