123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741 |
- 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
- {
- [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; }
- }
- protected virtual string documentationPage => name;
- public virtual string documentationURL => NodeUtils.GetDocumentationString(documentationPage);
- 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
- using(var tempSlots = PooledList<MaterialSlot>.Get())
- {
- GetInputSlots(tempSlots);
- // If no inputs were found use the precision of the Graph
- // This can be removed when parameters are considered as true inputs
- if (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 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
- 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
- using (var tempSlots = PooledList<MaterialSlot>.Get())
- {
- GetInputSlots(tempSlots);
- foreach (var inputSlot in 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);
- tempSlots.Clear();
- GetInputSlots(tempSlots);
- var inputError = 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
- tempSlots.Clear();
- GetOutputSlots(tempSlots);
- foreach (var outputSlot in 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;
- tempSlots.Clear();
- GetOutputSlots(tempSlots);
- isInError |= 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)
- {
- using (var tempSlots = PooledList<MaterialSlot>.Get())
- using (var tempPreviewProperties = PooledList<PreviewProperty>.Get())
- using (var tempEdges = PooledList<IEdge>.Get())
- {
- GetInputSlots(tempSlots);
- foreach (var s in tempSlots)
- {
- tempPreviewProperties.Clear();
- tempEdges.Clear();
- owner.GetEdges(s.slotReference, tempEdges);
- if (tempEdges.Any())
- continue;
- s.GetPreviewProperties(tempPreviewProperties, GetVariableNameForSlot(s.id));
- for (int i = 0; i < tempPreviewProperties.Count; i++)
- {
- if (tempPreviewProperties[i].name == null)
- continue;
- properties.Add(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();
- }
- }
- }
|